dot Redis 8 来了 - 而且是开源的

了解更多

如何使用 Redis 构建一个应用程序来增强你的日志分析能力

随着你的 IT 基础设施规模的增长和复杂性的增加,你可能不得不花费更多的精力来确保一切都得到妥善管理。

自从云计算兴起以来,这一点变得尤为重要,因为我们现在有大量技术信息分散在许多不同的位置。

但是,能够进行全面的日志分析可能既繁琐、耗时,又令人痛苦地无聊

摆脱这种负担的一个简单而有效的方法是使用专门用于简化日志监控的软件或应用程序……这正是 Alexis Gardin 在他的应用程序 Logub 中所做的。

与许多其他 SaaS 解决方案不同,Logub 可以在本地和开源方式下执行这些操作。 通过遵循以下步骤,你将能够创建一个应用程序,该应用程序将为你收集、浏览和分析应用程序日志。

该应用程序的核心是依赖 RediSearch 能够以无与伦比的效率浏览和分析不同位置的日志。 让我们来看看 Alexis 是如何将这个应用程序组合在一起的。

但在我们深入研究之前,我们应该让你知道,我们还在 Redis Launchpad 上提供了一系列令人兴奋的应用程序供你查看。

https://www.youtube.com/embed/6ZqfboCjLtk
  1. 你将构建什么?
  2. 你需要什么?
  3. 架构
  4. 开始使用
  5. 数据如何存储
  6. 数据如何查询
  7. 搜索栏如何工作?
  8. 如何将 Logub 集成到你的项目中?
  9. 结论

1. 你将构建什么?

你将构建一个由 Redis 驱动的特殊应用程序,用于收集、分析和浏览日志应用程序。 在下面的步骤中,我们将详细介绍构建此应用程序所需的 A-Z,以及所需的组件。

即使你是 Redis 的新手,我们也会向你展示如何掌握这个强大的数据库。 你准备好开始了吗?

好的,让我们直接进入。

2. 你需要什么?

  • Fluentd用作跨平台开源数据收集软件,用于处理日志。
  • Fluent 的 Redis 输出插件: fluent-plugin-redis 是一个 fluent 插件,用于输出到 redis。RediSearch
  • Docker用作开发人员将应用程序打包到容器的平台。
  • Docker Compose用于定义和运行多容器 Docker 应用程序。

3. 架构

  • Fluentd 从各个目的地的每个应用程序收集所有日志
  • 然后,Fluentd 格式化每个日志并将其发送到 Redis 进行存储
  • 数据使用 Fluentd Redis 插件存储 - 一个 fluent 插件,用于输出到 redis。
  • Logub 后端允许 RediSearch 执行全文搜索并索引用户定义的字段。

4. 开始使用

在演示中,一个 DEMO 应用程序将在 Logub 中发布日志。 你将能够与此 DEMO 应用程序交互以生成你的日志。 然后,你将能够在 Logub 中请求它们

先决条件

  • Docker
  • Docker Compose

首先,请确保给定的端口已在你的系统上打开:8080、8081、3000 和 6379。

步骤 1: 克隆存储库

git clone https://github.com/redis-developer/logub

步骤 2. 启动 DEMO 应用程序

  • 转到 /demo 文件夹 (cd ./demo)
  • 使用给定的命令启动 docker-compose
docker-compose up -d

启动 Logub 演示

% docker-compose ps
NAME                      COMMAND                  SERVICE             STATUS              PORTS
demo_fluentd_1            "tini -- /bin/entryp…"   fluentd             running             5140/tcp, 0.0.0.0:24224->24224/tcp, :::24224->24224/tcp, 0.0.0.0:24224->24224/udp, :::24224->24224/udp
demo_logub-controller_1   "java -XX:+UnlockExp…"   logub-controller    running             0.0.0.0:8080->8080/tcp, :::8080->8080/tcp
demo_logub-generator_1    "java -XX:+UnlockExp…"   logub-generator     running             0.0.0.0:8081->8081/tcp, :::8081->8081/tcp
demo_logub-ui_1           "docker-entrypoint.s…"   logub-ui            running             0.0.0.0:3000->3000/tcp, :::3000->3000/tcp
demo_redis_1              "redis-server /usr/l…"   redis               running             0.0.0.0:6379->6379/tcp, :::6379->6379/tcp

转到 localhost:3000 以浏览日志。

你可能需要大约一分钟才能看到日志的出现。 你可以通过使用右侧的侧边栏过滤它们来在页面上查看日志。 或者,你可以通过顶部的搜索栏通过过滤器或全文查询来搜索它们。

当你单击日志时,将显示详细信息,并且你可以选择索引业务属性。 这些业务属性可以用作以后的过滤器。

接下来,转到 localhost:3000/demo 以访问 playground 并添加你的自定义日志。 此演示页面将允许你:

  • 在演示假应用程序中创建假用户并在日志中查看它们。
  • 在系统中发布你的日志。

如果你返回主页,你可以尝试搜索你刚刚生成的日志。

注意:在容器中生成日志与其在 Logub 中的显示之间存在大约 1 分钟的延迟。 此延迟是由收集、格式化和将日志摄取到数据库中的过程引起的。

5. 它是如何工作的?

数据如何存储

数据使用 Fluentd Redis 插件存储。 它使用 HSET 存储每个日志。 例如:HSET level DEBUG message “Hello World” thread main

数据如何查询

public class LogubLog {
  @Builder.Default
  private String id = UUID.randomUUID().toString();

  @Builder.Default
  private String index = "principal";

  @NonNull
  private SystemProperties systemProperties;

  @Builder.Default
  private Map<String, Object> businessProperties = Collections.emptyMap();

  @Builder.Default
  private Optional<String> message = Optional.empty();

  @Builder.Default
  private Instant timestamp = Instant.now();

  @Builder.Default
  private Optional<String> service = Optional.empty();

  @Builder.Default
  private Optional<String> logger = Optional.empty();

  @Builder.Default
  private Optional<String> thread = Optional.empty();

  @Builder.Default
  private Optional<String> source = Optional.empty();

  @Builder.Default
  private LogLevel level = UNKNOWN;

}

public class SystemProperties {
  @Builder.Default
  Optional<String> imageName = Optional.empty();

  @Builder.Default
  Optional<String> containerName= Optional.empty();

  @Builder.Default
  Optional<String> containerId= Optional.empty();

  @Builder.Default
  Optional<String> env= Optional.empty();

  @Builder.Default
  Optional<String> host= Optional.empty();

}

你将使用此对象来操作日志并从 Redis 中检索它们。 执行此操作将允许 Logub UI 显示日志。 为了在你的日志中进行复杂的查询,你可以使用 RediSearch。

由于你可以动态更改 RediSearch 架构,你可以使用“List”数据结构来跟踪索引了哪些架构。

public class LogSearch {

 @Builder.Default
 private List<LogubFieldSearch> texts = emptyList();

 @Builder.Default
 private List<LogubFieldSearch> systemProperties = emptyList();

 @Builder.Default
 private List<LogubFieldSearch> businessProperties = emptyList();

 @Builder.Default
 private List<LogubFieldSearch> basicProperties = emptyList();

 @Builder.Default
 private List<LogubFieldSearch> levels = Collections.emptyList();

 @Builder.Default
 private int limit = 25;

 @Builder.Default
 private int offset = 0;

 @Builder.Default
 private Optional<LogubSort> sort = Optional.empty();

 @Builder.Default
 private Instant beginAt = Instant.now().minus(15, ChronoUnit.MINUTES);

 @Builder.Default
 private Instant endAt = Instant.now();


 @SneakyThrows
 public QueryBuilder toQuery() {
  var query = new QueryBuilder();
  var businessPrefix = "businessProperties.";
  var systemPropertiesPrefix = "systemProperties.";

  for (LogubFieldSearch properties : businessProperties) {
    query.append(QueryBuilders.tag(businessPrefix + properties.getName(), properties.getValues(),
    properties.isNegation()));
  }
  for (LogubFieldSearch properties : systemProperties) {
    query.append(QueryBuilders
    .tag(systemPropertiesPrefix + properties.getName(), properties.getValues(),
    properties.isNegation()));
  }
  for (LogubFieldSearch properties : basicProperties) {
    query.append(QueryBuilders
    .tag(properties.getName(), properties.getValues(),
    properties.isNegation()));
  }

  if (!levels.isEmpty()) {
    for (LogubFieldSearch level : levels) {
      var onError = !level.getValues().stream().allMatch(v -> Arrays.stream(LogLevel.values())
      .anyMatch(enumLevel -> enumLevel.name().equalsIgnoreCase(v)));
      if(onError){
        log.error("bad payload for levels {}", level);
        throw new IllegalArgumentException("bad payload for level");
      }
    query.append(QueryBuilders.tag("level",level.getValues(), level.isNegation()));
    }
  }
  for (LogubFieldSearch text : texts) {
    if(!text.getType().equals(LogubFieldType.FullText)){
      log.warn("type {} not handle for text search", text.getType());
    }
    for (String value : text.getValues()) {
      query.append(QueryBuilders.text("message", value, text.isNegation()));
    }
  }

  return query;
 }


}

上面的命令将允许你根据用户输入创建纯文本 RediSearch 查询。 在 RediSearch 库的顶部构建了一堆小的 QueryBuilders。 这与 Logub UI 将发送的命令相同,以便在你的日志中执行全面而高效的搜索。

步骤 2:如何使用搜索栏

你可以按标签或全文搜索执行搜索。 如果你遇到困难,可以使用以下示例作为模型。

  • env:dev Ut ea vero voluptate* 将搜索开发环境中所有消息以 Ut ea vero voluptate 开头的日志
  • -env:prod Ut ea vero voluptate* 将搜索除 prod 之外的所有环境中消息以 Ut ea vero voluptate 开头的所有日志
  • originRequest:France originRequest:USA 将搜索所有具有值为 France 或 USA 的字段 originRequest 的日志
  • “dog” “cat” 将搜索所有包含单词“dog”和“cat”的日志
  • -“dog” “cat” 将搜索所有不包含“dog”但包含“cat”的日志

为了让你有良好的应用程序测试体验,我们强烈建议你使用 playground 创建你自己的日志,添加业务属性,然后进行一些实验,只是为了感受一下它。

步骤 3:如何将 Logub 集成到你的项目中

你将需要以下 Docker 镜像列表来运行 Logub

Logub 日志格式

目前,Logub 只能处理一种特定的日志格式。 将来,此格式将扩展并更可自定义。

这是 Logub 格式

{
 "level": "....",
 "thread": "....",
 "logger": "....",
 "message": "....."
}

请注意,这些字段不是强制性的。

如果要添加你的业务属性,你需要添加一个以“mdc”作为键的嵌套 JSON 对象。 例如

{
 "level": "....",
 "thread": "....",
 "logger": "....",
 "message": ".....",
 "mdc":{
    "myproperties": "....",
    "anotherOne":".....",
 }
}

在 Logub 中发布日志

要在 Loghub 中浏览日志,你的容器需要使用 Docker Fluentd 日志记录驱动程序。 这是客户集成的配置示例。

# Your container(s)
  MY-CUSTOM-APP:
    image: "MY-CUSTOM-APP-IMAGE"
    logging:
      driver: "Fluentd"
      options:
        Fluentd-address: localhost:24224
        tag: MY-CUSTOM-TAG
  ## Logub Fluentd + Redis conf
  logub-controller:
    image: "logub/logub-controller:0.1"
    ports:
      - "8080:8080"
    depends_on:
      - redis
    links:
      - "Fluentd"
      - "redis"
  logub-ui:
    image: "logub/logub-ui:0.1"
    ports:
      - "3000:3000"
    depends_on:
      - redis
  Fluentd:
    image: "logub/logub-Fluentd:0.1"
    links:
      - "redis"
    ports:
      - "24224:24224"
      - "24224:24224/udp"
  redis:
    image: "redislabs/redismod"
    command: ["/usr/local/etc/redis/redis.conf","--bind","redis","--port", "6379"]
    volumes:
      - ./redis/data:/data
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
    ports:
      - "6379:6379"

步骤 4:在 Logub 中设置 Redis

RediSearch

Logub 使用 RediSearch 的功能来处理应用程序日志。 当日志保存在 Redis 数据库中时,它们会附带 3 种类型的字段

  • 系统属性:是 Docker 和 Fluentd 在发送日志时提供给我们的信息(例如,环境、容器名称等)。
  • 基本属性:是日志拥有的基本信息(例如,时间戳、服务等)。 这些属性会自动在 RediSearch 中建立索引。
  • 业务属性:由 Logub 用户在特定字段中提供。此处的用户必须遵循键值 (Map) 格式。 借助 RediSearch 的高级功能,您可以索引这些“业务属性”,以便对其进行研究。
{
  "timestamp": "2021-05-14 11:01:11.686",
  "level": "WARN",
  "thread": "scheduling-1",
  "mdc": {
    "app": "Toughjoyfax",
    "correlationId": "521f075f-36be-4f85-957e-d1c87ad71aa8",
    "originRequest": "Tonga",
    "origin": "LoremIpsum"
  },
  "logger": "com.loghub.loggenerator.service.LoggerService",
  "message": "Doloremque dolores ut minima sed."
}

这里有一个日志示例,描述了 Fluentd 如何在 Redis 数据库中展平和持久化时,我们的工具如何运行。 Logub API 将允许用户或公司索引 mdc 对象的一个或所有字段。

在这个项目中,Tag 数据类型被广泛使用。在日志中搜索时,通常会基于业务属性(例如客户 ID)进行搜索。 此外,我们还对日志消息使用 TextField 数据类型。 这允许用户在此字段中进行全文搜索。

以下是搜索过程的简化模式:

Redis

Redis 用于通过 Fluentd 将日志存储在 Redis 的 HashSet 类型中。

为了跟踪用户索引的字段,您还可以添加一个“schema”对象,该对象使用 Redis 的 List 类型

结论:使用 RediSearch 消除混乱

诸如云之类的数字创新爆炸式增长的一个缺点是,监控 IT 基础设施可能会变得复杂。应用程序可能分散在不同的位置,从而难以将每个日志提取到一个位置。

但是,通过使用 Redis,可以消除此过程的单调性,从而可以轻松地搜索、收集和存储日志。如果您想了解有关此应用程序的制作方式的更多信息,可以查看 Alexis 的YouTube 视频

我们还在我们的 Launchpad 上拥有一系列令人兴奋的创新应用程序,这些应用程序由像 Alex 这样的才华横溢的程序员开发。 查看一下,获得灵感,看看您可以使用 Redis 提出哪些创新。

谁构建了这个应用程序?

Alexis Gardin

Alexis 是一位创新的软件工程师,目前在 Zendoc 工作。前往 他的 GitHub 页面,看看他参与过的其他项目。