• 掌握结构化日志记录:全面指南


    在当今复杂的软件生态系统中,应用程序日志非常宝贵。它们允许开发者窥视应用程序的内部,了解系统内部的真实情况。但是,传统的非结构化日志数据有很多不足之处。这些混乱的文本块无法提供完整的画面。要真正发挥日志的力量,我们需要采取更加深思熟虑的方法 - 一种为了消费而设计的日志生成方式。
    在这里插入图片描述

    引言:

    这就是结构化日志记录登场的时刻。这种现代日志记录范式提供了解锁应用程序日志更深层次洞察力的缺失部分。与不透明的文本流不同,结构化日志在每条日志声明中嵌入重要的上下文元数据。这种机器可读的数据支持强大的搜索、过滤、分析和可视化。

    通过结构化日志记录,麻烦的错误和性能问题不会隐藏太久。应用程序行为的趋势和异常变得更加明显。将日志数据解析并引入分析工具变得简单。简而言之,结构化日志记录为应用程序日志记录的狂野世界带来了理智、理解和透明度。

    对于构建复杂、关键任务软件的开发者来说,结构化日志记录是观测性的必备工具。这篇博文深入探讨了结构化日志记录的关键概念及其为何应成为今天工程团队的最佳实践。我们将探索各种语言和框架的流行结构化日志记录库。通过理解结构化日志记录,你将提升设计、构建和监控在真实世界条件下运行良好的应用程序的能力。日志记录的未来已经到来 - 让我们一起探索吧!

    什么是结构化日志记录?

    结构化日志记录是一种现代的应用程序日志记录方法,旨在使日志数据更加可用。它通过输出结构化格式的日志声明,而不是非结构化的纯文本,来工作。

    每条日志行包含键值对和标准化字段,而不是自由格式的消息。例如,一个Web请求日志可能有像“timestamp”、“latency”、“request_id”、“user_id”、“endpoint”等字段。这提供了关于日志声明的关键上下文。

    所有相关的元数据都直接在日志行本身中,而不是埋藏在周围的文本中。这种机器可读的结构支持强大的搜索、过滤、聚合和可视化。结构化日志数据无缝集成到数据管道和分析工具中。

    结构化日志记录的好处:

    结构化日志记录的好处巨大。当你可以通过请求ID或用户ID过滤日志时,调试会变得更快。可以通过诸如延迟或CPU时间这样的字段监控性能模式和趋势。限速和聚合变得简单。上下文数据有助于调查问题。

    总的

    来说,结构化日志记录导致更高质量的日志数据,提供了更深入的洞察应用程序行为。它从将日志记录作为一个事后想法转变为针对消费和价值进行优化的日志。任何严肃的应用程序都可以通过采用结构化日志记录最佳实践获得观测性超能力。

    设置日志级别:

    日志记录的一个关键方面是设置适当的日志级别来控制日志输出的详细程度。常见的日志级别包括DEBUG、INFO、WARNING、ERROR和FATAL,每个级别在传达事件的严重性方面都有特定的用途。配置日志级别确保只捕获相关信息,提高日志清晰度并减少噪音。

    import "github.com/sirupsen/logrus"
    
    // 使用期望的日志级别配置日志记录器
    logrus.SetLevel(logrus.InfoLevel)
    
    • 1
    • 2
    • 3
    • 4

    如何使用日志级别映射:

    日志级别映射是日志记录器及其相应日志级别之间的映射。这种映射允许开发人员微调不同组件或环境的日志级别。通过根据上下文自定义日志级别,开发人员可以优先处理关键信息并抑制不那么重要的日志。

    var loggerLevels = map[string]logrus.Level{
        "module1": logrus.DebugLevel,
        "module2": logrus.InfoLevel,
    }
    
    func getLogger(module string) *logrus.Logger {
        logger := logrus.New()
        logger.SetLevel(loggerLevels[module])
        return logger
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    为不同环境使用不同日志级别的好处:

    为各种环境(如开发、测试和生产)采用不同的日志级别提供了几个优势。在开发中,详细的日志记录有助于调试工作,而在生产中,最小化日志详细程度可以节省资源并简化日志分析。根据每个环境调整日志级别优化了日志记录行为,提高了操作效率。

    使用结构化日志记录:

    实现结构化日志记录涉及集成支持结构化日志格式的日志记录库。通过利用像Go中的Slog这样的库,开发人员可以无缝过渡到结构化日志记录并解锁其好处。结构化日志记录库提供了用于格式化、过滤和丰富日志数据的强大功能,使开发人员能够深入洞察他们的应用程序。

    import "github.com/sirupsen/logrus"
    
    // 使用logrus的结构化日志记录示例
    logger := logrus.New()
    logger.WithFields(logrus.Fields{
        "user":    "john.doe",
        "action":  "login",
        "success": true,
    }).Info("用户成功登录")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如何实现自定义日志处理程序:

    自定义日志处理程序通过使开发人员能够自定义日志输出和行为来扩展日志记录库的功能。无论是添加额外的元数据、将日志发送到外部服务还是实现自定义日志格式化,自定义日志处理程序都提供了根据特定要求定制日志记录解决方案的灵活性。

    上下文日志记录:

    上下文日志记录通过向日志条目添加上下文信息(如请求ID、用户标识符和时间戳)来丰富日志条目。这些额外的元数据通过提供每个日志事件的关键上下文来增强日志的可追溯性,并简化根本原因分析。将上下文日志记录与中间件集成,允许开发人员自动将上下文注入日志

    条目,简化日志生成和分析。

    如何使用日志中间件将上下文信息附加到日志中:

    日志中间件与Web框架无缝集成,捕获特定于请求的信息并将其注入日志条目中。通过将中间件纳入请求处理管道,开发人员可以自动将上下文数据(如请求ID和用户代理)附加到日志消息中。这种方法提高了日志的可理解性,并促进了日志与特定应用程序交互之间的关联。

    在日志中拥有请求ID对调试目的的重要性:

    请求ID作为单个请求的唯一标识符,有助于在分布式系统中进行请求跟踪和调试。在日志条目中包含请求ID使开发人员能够追踪请求在各个组件中的流动,并识别瓶颈或错误。有了请求ID,故障排除变得更加容易,因为开发人员可以关联不同服务的日志,并重建每个请求的执行路径。

    存储您的日志:

    应用程序日志的存储方式可能成就或破坏日志数据的有用性。选择最佳存储解决方案需要评估规模、速度、保留需求和访问模式等因素。对于严肃的生产系统来说,简单地记录到本地文本文件的日子已经一去不复返了。现代应用程序需要强大的后端基础设施,以有效地利用日志。

    对于小规模用例,本地文件系统存储提供了简单性和易访问性。但随着数据量、用户和基础设施的增长,管理性、耐用性和聚合能力在管理性、耐用性和聚合能力方面出现了局限性。这就是集中式日志服务大放异彩的地方。

    云托管平台,如AWS CloudWatch日志和Datadog,提供了从多样化基础设施自动实时日志聚合的功能。弹性可伸缩性、细粒度访问控制和强大的分析功能使得可以获得可操作的洞察力。然而,随着数据量的增加,成本也会增加。

    开源日志存储,如Elasticsearch、Kafka和Redis,在本地或跨云环境中提供了类似的好处。它们可以集成到可扩展的日志管道中。但它们需要专业知识来操作、调整和保护。

    法规合规性也应指导日志存储选择。一些框架,如HIPAA,规定了数据保护、保留政策和访问审计,这些并非所有解决方案都完全支持。

    通过评估其独特的工作负载需求,DevOps团队可以选择日志存储架构,以获得最佳效用。本地文件的尾部提供了简单性,而基于云的分析揭示了隐藏的应用程序洞察力。结合两种方法将两全其美。

    推荐存储日志的地方(文件、Cloudwatch、Data Dog):

    对于中小型应用程序,将日志文件存储在本地可以是一个好选择。这在开发环境中提供了简单性。日志文件可以实时跟踪并直接访问以进行检查。然而,随着应用程序规模和复杂性的增长,本地日志文件变得更难管理。

    以下是在Go中将消息记录到文件的简单示例:

    package main
    
    import (
      "log"
    
    
      "os"
    )
    
    func main() {
    
      // 打开日志文件
      f, err := os.OpenFile("app.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
      if err != nil {
        log.Fatal(err)
      }
    
      // 设置日志记录器以记录到文件
      logger := log.New(f, "", log.LstdFlags)
    
      // 记录消息
      logger.Println("启动应用程序...")
      logger.Printf("日志消息:%s", "Hello World")
    
      // 关闭文件
      f.Close()
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    关键步骤是:

    1. 使用os.OpenFile()打开一个用于写入的文件。这将追加并在需要时创建文件。
    2. 创建一个写入打开文件的log.Logger
    3. 调用像Println()Printf()这样的logger方法来记录消息。
    4. 完成后关闭文件。

    这会将消息记录到app.log文件中。文件轮转、日志分割和其他增强功能可以在这个基本日志记录之上添加。

    对于规模较大的生产系统,AWS CloudWatch日志等基于云的日志聚合服务变得更加可取。CloudWatch提供了自动化的日志收集、高度可扩展的存储以及与其他AWS服务的集成。监控、警报和流式传输到其他数据平台等有用功能是可用的。通过日志过期策略等功能,成本可以优化。

    以下是如何在Go中将应用程序日志发送到AWS CloudWatch日志的示例:

    package main
    
    import (
      "github.com/aws/aws-sdk-go/aws"
      "github.com/aws/aws-sdk-go/aws/session"
      "github.com/aws/aws-sdk-go/service/cloudwatchlogs"
    
      "log"
    )
    
    func main() {
    
      // 创建CloudWatch日志客户端
      sess := session.Must(session.NewSessionWithOptions(session.Options{
        SharedConfigState: session.SharedConfigEnable,
      }))
    
      svc := cloudwatchlogs.New(sess)
    
      // 日志组和流名称
      logGroupName := "myAppLogs"
      logStreamName := "appLogStream"
      
      // 如果它们不存在,则创建日志组和流
      svc.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{
        LogGroupName: aws.String(logGroupName), 
      })
    
      svc.CreateLogStream(&cloudwatchlogs.CreateLogStreamInput{
        LogGroupName: aws.String(logGroupName),
        LogStreamName: aws.String(logStreamName),
      })
    
      // 记录消息
      logger := log.New(os.Stdout, "", log.LstdFlags) 
      logger.Println("应用程序启动")
    
      // 将日志发送到CloudWatch
      _, err := svc.PutLogEvents(&cloudwatchlogs.PutLogEventsInput{ 
        LogGroupName: aws.String(logGroupName),
        LogStreamName: aws.String(logStreamName),
        // 此处添加日志事件
      })
    
      if err != nil {
        logger.Printf("发送日志时出错:%v\n", err)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    关键步骤是:

    1. 使用AWS SDK创建CloudWatch日志客户端。
    2. 如果它们还不存在,则创建日志组和流。
    3. 本地记录消息。
    4. 使用PutLogEvents API调用将日志事件发送到CloudWatch。

    这允许将应用程序日志发送到CloudWatch,无需管理文件上传。其他功能,如Kinesis流摄取、报警、保留等,也可以配置。

    其他托管日志管理平台,如DatadogLogz.ioSumoLogic等,也为大型或关键任务系统提供了强大的企业功能。高级分析、可视化、归档功能和基于机器学习的警报规则很常见。它们允许跨本地、云和混合环境集中日志。成本根据引入的数据量而变化。

    以下是如何使用其Go库将日志从Go应用程序发送到Datadog的示例:

    package main
    
    import (
      "github.com/DataDog
    
    /datadog-go/statsd"
      datadog "github.com/DataDog/datadog-go/v5/datadog"
    )
    
    func main() {
    
      // 创建Datadog客户端
      ddClient, err := datadog.NewClient("", "")
    
      if err != nil {
        log.Fatal(err) 
      }
    
      // 创建statsd客户端
      statsd := statsd.New("127.0.0.1:8125")
    
      // 记录一些指标
      statsd.Gauge("requests.count", 100, []string{"version:1.0"}, 1) 
    
      // 发送日志
      ddClient.Logs.Send(&datadog.LogsPayload{
        Series: []datadog.LogsSeries{
          {
            Context: "application",
            Source: "go",
            Service: "app",
            Status: datadog.LogsStatusInfo,
            Tags: []string{"env:dev"}, 
            Logs: []datadog.LogsMessage{
              {
                Message:   "App started",
                Timestamp: datadog.CurrentEpochTime(),
              },
            },
          },
        },
      })
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    关键步骤是:

    1. 使用API和应用密钥创建Datadog客户端。
    2. 创建Statsd客户端以获取自定义指标。
    3. 通过客户端将指标和日志发送到Datadog的平台。

    这允许从Go应用程序发送指标和日志到Datadog的平台,进行监控和分析。

    对于微服务架构,建议使用分布式日志管道。每个应用程序本地记录日志并流式传输到中央聚合器,如Kafka或Fluentd。这避免了中央依赖或瓶颈。Graylog和ELK堆栈等开源选项在这里表现良好。

    总之,在选择日志存储解决方案时,评估规模、成本、团队技能和法规要求等因素。从本地文件开始简单,然后根据需要演变为云服务或自我管理平台。这在易用性和强大性之间为系统增长的长期日志管理平衡。

    结论:

    结构化日志记录代表了日志记录实践的范式转变,提供了增强的可读性、可搜索性和分析能力。通过采用结构化日志记录并利用Slog等工具和日志中间件,开发人员可以简化日志管理,深入洞察应用程序行为,并加快故障排除工作。拥抱结构化日志记录使开发人员能够释放他们日志数据的全部潜力,提升他们的应用程序监控和调试工作流程。

    关键点回顾:

    • 结构化日志记录将日志消息组织成标准化格式,提高了可读性和分析性。设置适当的日志级别确保只捕获相关信息,优化了日志清晰度和效率。
    • 使用像Go中的Slog这样的结构化日志记录库可以无缝集成并解锁高级日志记录功能。
    • 上下文日志记录通过添加额外的元数据来丰富日志条目,便于故障排除和根本原因分析。
    • 选择正确的日志存储解决方案取决于可扩展性、可访问性和合规性要求等因素。
  • 相关阅读:
    Linux 错误处理(字符设备基础三)
    数字电路和模拟电路-10时序逻辑电路的分析和设计
    Python高级学习笔记(三)—— 闭包和装饰器
    java毕业设计——基于java+Java Swing+jsp的企业快信系统设计与实现(毕业论文+程序源码)——企业快信系统
    【算法|双指针系列No.3】leetcode202. 快乐数
    交换机端口汇聚详解
    取得PMP证书需要多长时间?
    数据可视化项目1
    【Linux篇】gdb的使用
    数据链路层(必备知识)
  • 原文地址:https://blog.csdn.net/csjds/article/details/136380205