Laravel的审计和事件日志处理器

1.1 2024-03-02 21:57 UTC

This package is auto-updated.

Last update: 2024-09-23 14:56:23 UTC


README

[[目录]]

概览

Audit包是一个开源的Composer包,用于Laravel应用程序,由其他Provisionesta包使用,以提供一致的日志语法,并添加对时间敏感事件数据库事务(即将发布)的支持。尽管这是为我们的包专门设计的,但您也可以将其用于自己的标准化日志。

此包由开源社区维护,不由任何公司维护。请自行承担风险,并创建合并请求来解决您遇到的任何错误。

问题陈述

当使用Laravel日志Log::info('Message', ['key1' => 'value', 'key2' => 'value'])语法时,很容易出现日志格式不一致,从而导致各种日志消息和不同的上下文键。

Provisionesta\Audit\Log::create()方法提供一组预定义的上下文键,使我们能够改进外部日志平台中的索引和可搜索性,并确保所有事件都以一致的方式提供尽可能多的上下文数据。

有时您需要获取一个可以添加到变更日志或程序化操作的格式化数组,而不是尝试查看日志文件。每次创建日志条目时都会返回一个数组。

(即将发布)由于一些日志事件需要执行,此包添加了对audit_transactions数据库表的支持,允许您像管理后台作业队列一样集中管理,并根据您的应用程序和业务需求触发不同的操作和工作流程。

问题跟踪和错误报告

我们不维护功能请求路线图,但欢迎您贡献,我们乐意审查您的合并请求。

请创建问题来报告错误。

贡献

请参阅CONTRIBUTING.md了解如何贡献的更多信息。

维护者

姓名GitLab昵称电子邮件
Jeff Martin@jeffersonmartinprovisionesta [at] jeffersonmartin [dot] com

贡献者致谢

安装

需求

需求版本
PHP^8.0
Laravel^8.0, ^9.0, ^10.0, ^11.0

升级指南

查看变更日志以获取发布说明。

添加Composer包

composer require provisionesta/audit:^1.1

如果您正在为此包做出贡献,请参阅CONTRIBUTING.md了解如何配置具有符号链接的本地composer包的说明。

发布配置文件

这是可选的。如果将解析的日志条目作为变量返回,则使用自定义模式的配置文件。

php artisan vendor:publish --tag=audit

使用示例

基本用法

use Provisionesta\Audit\Log;

Log::create(
    event_ms: $event_ms,
    event_type: 'okta.api.post.success.ok',
    level: 'info',
    message: 'Success',
    metadata: [
        'okta_request_id' => 'REDACTED',
        'rate_limit_remaining' => $request->headers['x-rate-limit-remaining'],
        'uri' => 'users',
        'url' => 'https://dev-12345678.okta.com/api/v1/users?activate=true'
    ],
    method: __METHOD__
);

综合用法

您可以将此示例复制粘贴到代码中创建日志条目的任何位置。任何不相关的参数都可以删除,将被视为null。

use Provisionesta\Audit\Log;

Log::create(
    attribute_key: 'xxx',
    attribute_value_old: 'xxx',
    attribute_value_new: 'xxx',
    count_records: count($array),
    dump: false,
    dump_keys: [],
    duration_ms: $duration_ms,
    duration_ms_per_record: (int) ($duration_ms / count($records)),
    errors: [],
    event_ms: $event_ms,
    event_ms_per_record: (int) ($event_ms / count($records)),
    event_type: '{provider}.{entity}.{action}.xxx',
    level: 'info',
    log: true,
    message: '{What happened}',
    metadata: [],
    method: __METHOD__,
    occurred_at: $entity->created_at,
    parent_id: $parent->id,
    parent_type: 'App\\Models\\{Provider}\\Application',
    parent_reference_key: 'name',
    parent_reference_value: $entity->organization->name,
    record_id: $entity->id,
    record_type: 'App\\Models\\{Provider}\\{Entity}',
    record_provider_id: $entity->provider_id,
    record_reference_key: 'name',
    record_reference_value: $entity->name,
    tenant_id: $entity->provider_organization_id,
    tenant_type: 'App\\Models\\{Provider}\\Organization',
    transaction: false
);

示例输出

[YYYY-MM-DD HH:II:SS] local.DEBUG: ApiClient::post Success {"event_type":"okta.api.post.success.ok","method":"Provisionesta\\Okta\\ApiClient::post","event_ms":627,"metadata":{"okta_request_id":"REDACTED","rate_limit_remaining":"16","uri":"users","url":"https://dev-12345678.okta.com/api/v1/users?activate=true"}"}
{
    "event_type": "okta.api.post.success.ok",
    "method": "Provisionesta\\Okta\\ApiClient::post",
    "event_ms": 627,
    "metadata": {
        "okta_request_id": "REDACTED",
        "rate_limit_remaining": "16",
        "uri": "users",
        "url": "https://dev-12345678.okta.com/api/v1/users?activate=true"
    }
}

日志参数定义

参数名称 示例用法 描述
event_type (必填)
字符串
provider.entity.action.result.reason 遵循我们编码风格约定的八位字节表示的事件类型。
level (必填)
字符串
调试
信息
注意
警告
错误
严重
警报
紧急
日志条目的日志级别
message (必填)
字符串
验证失败 要在日志中包含的简短消息。这将被自动添加前缀
 with the fully-qualified method name (the "noun") so you can keep
 the message focused on the "verb" language.</td>

method (必填)
字符串
METHOD 在此审计日志创建处或代表的地方的方法。
attribute_key
字符串
(状态更改) 已更改的数据库列名。
attribute_value_old
字符串
(状态更改) 更新前的数据库中的值。
attribute_value_new
字符串
(状态更改) 现在在数据库中更新的API值。
count_records
int
count($array) (多记录) 处理的记录数。
dumpconfig
string
默认 (响应模式) 包含 datekeysstrings 模式配置config/audit.php 中的数组键。如果设置了 dump_config,则忽略其他 dump* 参数。
dump_date
字符串
c
Y-m-d
Y-m-d H:i:s
(响应模式) 响应数组中返回的时间戳的 PHP 日期时间格式字符串。
dump_keys
数组
文档 (响应模式) 响应数组中从 Log::create() 方法返回的键的筛选列表。
dump_strings
数组
文档 (响应模式) 应包含在响应数组中的静态字符串键值对的数组(而不是稍后通过收集转换自己添加)。
duration_ms
Carbon
$duration_ms Carbon 实例(时间戳)用于长时间运行的批处理作业,以提供自作业开始以来的点时间持续时间。
duration_ms_per_record
int
(int) ($duration_ms / count($records)) 毫秒数除以记录数。这不会自动计算,以允许自定义 Carbon 时间戳的灵活性
errors
数组
错误消息的平面数组,将被编码为 JSON
event_ms
Carbon
$event_ms Carbon 实例(时间戳)在操作的开始时初始化,并为长时间运行的作业中此特定操作提供点时间持续时间。
event_ms_per_record
int
(int) ($event_ms / count($records)) 毫秒数除以记录数。这不会自动计算,以允许自定义 Carbon 时间戳的灵活性
job_batch
字符串
(后台作业日志) 作业批次的人类标识字符串或系统 ID。格式由您自行决定。
job_id
字符串
(后台作业日志) 触发此日志条目的特定作业的人类标识字符串或系统 ID。格式由您自行决定。
job_platform
字符串
github
gitlab
lambda
redis
{your string}
(后台任务日志) 运行后台任务的平台的标识符字符串。格式由您自行决定。
job_pipeline_id
字符串
(后台任务日志) CI/CD 管道的系统 ID(如有适用)。
job_timestamp
字符串
now()->getTimestamp() (后台任务日志) 任务或管道开始的时间戳。这对于识别哪个计划的作业时间戳触发了此事件很有用。
job_transaction_id
字符串
now()->getTimestamp() (后台任务日志) 与 job_id 相对的替代方案,可以用于应用程序或业务逻辑中使用的其他可索引标识符。
log
bool
true (默认)
false
是否为该事件创建系统日志条目。请参阅文档
metadata
数组
一个包含应包含在日志中的自定义元数据的数组
occurred_at
字符串
2023-02-01T03:45:27.612584Z 基于创建时间或更新时间的 API 时间戳,将使用 Carbon 格式化的事件发生的时间戳
parent_id
字符串
{uuid} (多对多关系事件) 具有多对多关系的数据库模型的数据库 ID。
parent_type
字符串
App\Models\Path\To\ModelName (多对多关系事件) 具有多对多关系的数据库模型的完全限定命名空间。
parent_provider_id
字符串
a1b2c3d4e5f6 (多对多关系事件) 具有多对多关系的数据库模型的 API ID,通常存储在数据库中的 provider_id 列中。
parent_reference_key
字符串
name (多对多关系事件) 日志中可读的数据库列的名称
parent_reference_value
字符串
(多对多关系事件) 可读数据库列的值
record_id
字符串
{uuid} 受影响的数据库模型的数据库 ID
record_type
字符串
App\Models\Path\To\ModelName 数据库模型的完全限定命名空间
record_provider_id
字符串
a1b2c3d4e5f6 受影响的数据库模型的 API ID,通常存储在数据库中的 provider_id 列中。
record_reference_key
字符串
name 日志中可读的数据库列的名称
record_reference_value
字符串
可读数据库列的值
tenant_id
字符串
{uuid} 顶级组织/租户的数据库 ID
tenant_type
字符串
App\Models\VendorName\Organization 顶级实体(组织、租户等)的数据库模型的完全限定命名空间。
transaction
bool
true
false (默认)
是否为该事件创建事务数据库条目

高级用法

后台任务日志条目

如果您正在运行后台任务并且想要将元数据添加到日志和事务中,您可以添加 job_* 参数。所有这些值(除 job_timestamp 外)都是自由格式字符串,您可以根据需要标准化。

use Provisionesta\Audit\Log;

Log::create(
    // ...
    job_batch: '{string}',
    job_id: '{string}',
    job_platform: '{string}',
    job_pipeline_id: '{string}',
    job_timestamp: now()->getTimestamp(),
    job_transaction_id: '{string}',
    // ...
);

跳过日志创建

您可以指定 logtransaction 参数的真/假布尔值。默认情况下,logtruetransactionfalse

解析和格式化的模式始终以数组的形式返回。

logtransaction行为
truefalse(默认) 创建日志条目。
truetrue创建日志条目。为事务创建数据库行。
falsetrue不创建日志。为事务创建数据库行。
falsefalse不创建日志或事务数据库行。用于模式解析。

响应模式

此包的一个优点是具有可预测键的格式化数组。

作为创建事务的替代方案,您也可以返回一个格式化的数组,包含所有提供的键及其值(或其默认值)。如果您只需要一个数组用于自己的变更日志或其他用途,这将非常有用。您还可以禁用日志创建,并像使用数据传输对象(DTO)一样使用它。

只需定义一个变量以获取返回的数组。

use Provisionesta\Audit\Log;

// Create a log entry with no returned array
Log::create(
    // ...
    log: true
    // ...
);

// Define a variable with the returned array
$schema = Log::create(
    // ...
    log: true
    // ...
);

// Append the return array to an existing array of records that are not created in system logs
foreach($records as $record) {
    // ...

    $changelog[] = Log::create(
        // ...
        log: false
        // ...
    );
}

无配置的示例响应数组

use Provisionesta\Audit\Log;

$event_ms = now();

$result = Log::create(
    event_ms: $event_ms,
    event_type: "okta.api.post.success.ok",
    level: "info",
    message: "Success",
    metadata: [
        "okta_request_id" => "REDACTED",
        "rate_limit_remaining" => "199",
        "uri" => "users",
        "url" => "https://dev-12345678.okta.com/api/v1/users?activate=true"
    ],
    method: "Provisionesta\Okta\ApiClient::get"
);

dd($result);

// [
//     "datetime" => "2024-03-02T18:51:10+00:00",
//     "event_type" => "okta.api.post.success.ok",
//     "level" => "info",
//     "message" => "Success",
//     "method" => "Provisionesta\Okta\ApiClient::get",
//     "attribute_key" => null,
//     "attribute_value_old" => null,
//     "attribute_value_new" => null,
//     "count_records" => null,
//     "duration_ms" => null,
//     "duration_ms_per_record" => null,
//     "errors" => [],
//     "event_ms" => 4,
//     "event_ms_per_record" => null,
//     "job_batch" => null,
//     "job_id" => null,
//     "job_platform" => null,
//     "job_pipeline_id" => null,
//     "job_timestamp" => null,
//     "job_transaction_id" => null,
//     "metadata" => [
//       "okta_request_id" => "REDACTED",
//       "rate_limit_remaining" => "199",
//       "uri" => "users",
//       "url" => "https://dev-12345678.okta.com/api/v1/users?activate=true",
//     ],
//     "occurred_at" => null,
//     "parent_id" => null,
//     "parent_type" => null,
//     "parent_provider_id" => null,
//     "parent_reference_key" => null,
//     "parent_reference_value" => null,
//     "record_id" => null,
//     "record_type" => null,
//     "record_provider_id" => null,
//     "record_reference_key" => null,
//     "record_reference_value" => null,
//     "tenant_id" => null,
//     "tenant_type" => null,
// ]

在响应数组中指定键

模式中存在大量参数键。为了避免使用Laravel Collections的transform方法处理结果数组,您可以简单地将要包含的键数组传递给dump_keys数组。

use Provisionesta\Audit\Log;

$result = Log::create(
    // ...
    dump_keys: [
        'event_type',
        'message',
        'attribute_key',
        'attribute_value_old',
        'attribute_value_new',
        'record_id',
        'record_type',
        'record_provider_id',
        'record_reference_key',
        'record_reference_value'
    ],
);

dd($result);

// [
//     "datetime" => "2024-03-02T18:53:35+00:00",
//     "event_type" => "okta.api.post.success.ok",
//     "message" => "Success",
//     "attribute_key" => null,
//     "attribute_value_old" => null,
//     "attribute_value_new" => null,
//     "record_id" => null,
//     "record_type" => null,
//     "record_provider_id" => null,
//     "record_reference_key" => null,
//     "record_reference_value" => null,
// ]

在响应数组中指定自定义静态字符串

如果您需要向数组中添加自定义静态字符串,它们可以在dump_strings数组中指定。字符串将返回到数组末尾。

use Provisionesta\Audit\Log;

$result = Log::create(
    // ...
    dump_keys: [
        'event_type',
        'message',
        'attribute_key',
        'attribute_value_old',
        'attribute_value_new',
        'record_id',
        'record_type',
        'record_provider_id',
        'record_reference_key',
        'record_reference_value'
    ],
    dump_strings: [
        'custom_key' => 'my_value',
        'another_key' => 'my_value',
    ],
    // ...
);

dd($result);

// [
//     "datetime" => "2024-03-02T18:54:55+00:00",
//     "event_type" => "okta.api.post.success.ok",
//     "message" => "Success",
//     "attribute_key" => null,
//     "attribute_value_old" => null,
//     "attribute_value_new" => null,
//     "record_id" => null,
//     "record_type" => null,
//     "record_provider_id" => null,
//     "record_reference_key" => null,
//     "record_reference_value" => null,
//     "custom_key" => "my_value",
//     "another_key" => "my_value",
// ]

响应数组中的自定义日期时间格式

datetime始终返回为数组的第一个键(表列)。您可以使用支持的PHP日期时间格式字符来自定义格式。默认情况下,我们使用c来表示ISO 8601格式(YYYY-MM-DDTHH:II:SS+00:00)。您可能想将其简化为Y-m-dY-m-d H:i

use Provisionesta\Audit\Log;

$result = Log::create(
    // ...
    dump_date: 'c',
    dump_keys: [
        'event_type',
        'message',
        'attribute_key',
        'attribute_value_old',
        'attribute_value_new',
        'record_id',
        'record_type',
        'record_provider_id',
        'record_reference_key',
        'record_reference_value'
    ],
    dump_strings: [
        'custom_key' => 'my_value',
        'another_key' => 'my_value',
    ],
    // ...
);

dd($result);

// [
//     "datetime" => "2024-03-02",
//     "event_type" => "okta.api.post.success.ok",
//     // ....
// ]

响应数组的标准化配置

提示:您需要发布配置文件,以便它在config/audit.php中显示。如果不发布,它将使用vendor/provisionesta/audit目录中的默认配置,该配置无法修改。

管理代码库中的简化模式可能会很困难。

您可以在config/audit.php文件中为每个用例定义标准化的简化模式。default键是一个占位符,可以自定义,并且如果需要,您可以添加额外的数组以处理每种类型的资源(例如okta_user)。

定义模式后,只需将dump_config键设置为其在config/audit.php中定义的相同键。

use Provisionesta\Audit\Log;

$result = Log::create(
    // ...
    dump_config: 'okta_user'
    // ...
);
// config/audit.php

return [
    'dump' => [
        'default' => [
            'date' => 'c'
            'strings' => [
                'custom_key' => 'my_value'
            ],
            'keys' => [
                'event_type',
                'message',
                'record_id',
                'record_type',
                'record_provider_id',
                'record_reference_key',
                'record_reference_value'
            ],
        ],
        'okta_user' => [
            'date' => 'c',
            'strings' => [
                'custom_key' => 'my_value'
            ],
            'keys' => [
                'event_type',
                'message',
                'attribute_key',
                'attribute_value_old',
                'attribute_value_new',
                'record_id',
                'record_type',
                'record_provider_id',
                'record_reference_key',
                'record_reference_value'
            ]
        ]
    ]
];

响应数组的实际示例

use Provisionesta\Audit\Log;

$result = Log::create(
    attribute_key: $attribute,
    attribute_value_old: $manifest_record[$attribute],
    attribute_value_new: $api_record[$attribute],
    duration_ms: $this->duration_ms,
    event_type: $this->event_type . '.datadumper.manifest.attribute.changed.' . $attribute,
    level: 'info',
    log: true
    message: Str::title($attribute) . ' Attribute Value Changed',
    method: __METHOD__,
    record_provider_id: $manifest_record['provider_id'],
    record_reference_key: $this->reference_key,
    record_reference_value: $manifest_record[$this->reference_key] ?? null,
    transaction: true
);

dd($result);

// [
//     'datetime' => 'YYYY-MM-DDTHH:II:SS+00:00',
//     'event_type' => 'okta.user.sync.datadumper.manifest.attribute.changed.manager',
//     'message' => 'Manager Attribute Value Changed',
//     'attribute_key' => 'manager',
//     'attribute_value_old' => 'klibby',
//     'attribute_value_new' => 'dmurphy',
//     'record_id' => null,
//     'record_type' => 'okta_user',
//     'record_provider_id' => '00u1b2c3d4e5f6g7h8i9',
//     'record_reference_key' => 'handle',
//     'record_reference_value' => 'jpardella'
//     'custom_key' => 'my_value',
// ]