20 个版本

0.2.5 2021 年 4 月 29 日
0.2.4 2020 年 7 月 7 日
0.2.3 2020 年 6 月 16 日
0.2.1 2020 年 1 月 29 日
0.1.5 2019 年 7 月 26 日

#282 in 调试


4 个代码包中使用(通过 realm

MIT 许可证

60KB
1.5K SLoC

观察者

** Observer 是一个用于捕获 Rust 服务器可观察性的库

Observer 2.0 在行动

要使用 observer 2.0

  1. Cargo.toml 中添加依赖项。
observer = "0.2.0"
observer_attribute = "0.1.6"
  1. 将这些依赖项导入 lib.rs
extern crate observer;
#[macro_use]
extern crate observer_attribute;
  1. 定义事件 JSON 文件并导出事件路径为 EVENTS_PATH。
export EVENTS_PATH="<Path of events.json file>"

event.json 文件

{
    "foo__create_temp" : {
      "critical" : true,
      "result_type": "list",
      "fields" : {
        "id" : "string"
      }
    },
    "update_temp" : {
      "critical" : false,
      "result_type": "i32",
      "fields" : {
        "id" : "string"
      }
    }
}
  1. 在项目中使用 observer
// Here namespace and with_result are optional parameter.
// If define namespace so in event file needs to define `foo__create_policy`
// If with_result defined so it log error also, if function returns an error.
use observer::prelude::*;
use observer::Result;

pub struct Temp;

#[observed(with_result)]
pub fn update_temp(id: &str) -> observer::Result<Temp> {
    observe_field("id", id); // Need to tell type of id's value in event.json
    observe_result(2314);  // Need to tell type of result in event.json
    observer::observe_span_log("Message from update temp");
    Ok(Temp)
}

#[observed(namespace = "foo")]
pub fn create_temp(id: &str) -> observer::Result<Temp> {
    observe_field("id", "4839");
    observe_result(&vec![1,2,3,4]);
    update_temp(id)
}

fn main(){
    // define logger
    let logger = observer::backends::logger::Logger::builder()
            .with_path("/tmp/observer.log")
            .with_stdout()
            .build();

    // Initialize observer with logger
    observer::builder(Box::new(logger))
            .create_context("main")
            .init();

    // Call your functions
    let _result = create_temp("temp");

    // End of the observer.
    observer::end_context();

}

stdout 中应该是这样的

logger_initialized
context: main [0ms, 2020-01-29 11:10:54.728594 UTC]
    foo__create_temp: 0ms
        @id: "4839"
        @@success: true
        #result: [1,2,3,4]
        update_temp: 0ms
            @id: "temp"
            @@success: true
            #result: 2314
            logs:
               - 0ms: Message from update temp

在日志文件中应该看起来一样。

logger_initialized
context: main [0ms, 2020-01-29 11:10:54.728594 UTC]
    foo__create_temp: 0ms
        @id: "4839"
        @@success: true
        #result: [1,2,3,4]
        update_temp: 0ms
            @id: "temp"
            @@success: true
            #result: 2314
            logs:
               - 0ms: Message from update temp

Observer 0.1.* 在行动

要使用 Observer

  1. 必须定义事件文件(json 文件,强制)。
  2. 必须定义日志目录,否则它将默认为 /var/log/

首先,我们必须定义要观察的函数的事件。在这里,事件与函数名相同,在事件中,我们必须告诉哪些字段需要保存到面包屑中。关键意味着是否要保存此函数到本地或队列。如果是关键的,它将直接进入队列,否则 Observer 将将其保存到本地。

这里我们定义了两个事件 observer_meobserve_me_too(与函数名相同)。

{
    "observe_me" : {
        "critical" : true,
        "fields" : {
            "foo" : "String",
            "foo1" : "f32"
        }
    },
    "observe_me_too" : {
        "critical" : false,
        "fields" : {
            "foo1" : "i32"
        }
    }
}
// src/bin/main.rs
use observer::{
    context::{observe_string, observe_i32, observe_f32},
    observe::observe,
    queue::Queue,
};

#[observed] // Need to define only this on top of fn which we want to observe
// Context reference is mandatory to pass in observer function.
// fn should be return Result type.
fn observe_me(ctx: &Context, other_params: i32)-> Result<String> {
    // in "foo" can store only string value else it will give compile error
    // It will this in breadcrumbs in Frame
    observe_field("foo", "value".to_string());
    // in "foo1" can store only f32 value else it will give compile error
    // It will this in breadcrumbs in Frame
    observe_field("foo1", 32.02);
    some_other_fn_call();

    // Observing this fn also and it will become a sub-frame of observe_me
    observe_me_too(ctx);
    Ok("observed")
}

fn some_other_fn_call() {}

#[observed]
fn observe_me_too(ctx: &Context) -> Result<i32> {
    observe_field("foo1", 32);
    Ok(12)
}


#[derive(Serialize, Debug, Deserialize)]
pub struct DemoQueue {
    pub name: String,
}

#[typetag::serde(name = "Abc")]
impl Queue for DemoQueue {
    // TODO: Will give complete definition of in next version surely
    fn enqueue(&mut self, data: serde_json::Value) {
        println!("Data: {}", data)
    }
}


fn main() {
    let ctx = Context::new(
        "test_context".to_string(),
        Box::new(DemoQueue{name: "Abrar".to_string()})
    );
    let _result = observe_me(&ctx, 12);
    ctx.finalise();
}

我们首先调用 observer_me,然后在其中调用 observer_me_too。在上下文对象的情况下,Observer 将创建 observer_me 和 observer_me_too 的框架。因为 observer_me_too 是从 observer_me 内部调用的,所以它将成为 observer_me 的子框架。

它将根据事件的临界性创建日志目录,并将上下文保存到 log_dir_path/context,事件保存到 log_dir_path/observe_me 和 log_dir_path/observe_me_too。

上下文日志将看起来像这样

{
  "frame": {
    "breadcrumbs": {},
    "key": "17eb437f-a5e2-4243-8dac-fa636429dcf9",
    "result": null,
    "sub_frames": [
      {
        "breadcrumbs": {
          "foo": 32
        },
        "key": "59471fc8-3391-4619-b341-931658a2296e",
        "result": 12,
        "sub_frames": [
          {
            "breadcrumbs": {
              "foo1": 32.02
            },
            "key": "399c8d43-16fb-4cd3-8273-b2666026f2f0",
            "result": "observed",
            "sub_frames": [],
            "success": true,
            "end_time": "2019-07-06T08:27:20.451786Z",
            "id": "observe_me_too",
            "start_time": "2019-07-06T08:27:20.451642Z"
          }
        ],
        "success": true,
        "end_time": "2019-07-06T08:27:20.452680Z",
        "id": "observe_me",
        "start_time": "2019-07-06T08:27:20.451618Z"
      }
    ],
    "success": null,
    "end_time": "2019-07-06T08:27:20.452683Z",
    "id": "main",
    "start_time": "2019-07-06T08:27:20.451590Z"
  },
  "key": "302a5760-107a-4826-8670-2efd57db27c2",
  "queue": {
    "type": "Abc",
    "value": {
      "name": "Abrar"
    }
  },
  "id": "test_context"
}

框架/事件日志将看起来像这样

{
  "key": "399c8d43-16fb-4cd3-8273-b2666026f2f0",
  "id": "observe_me_too",
  "breadcrumbs": {
    "foo1": 32.02
  },
  "end_time": "2019-07-06T08:27:20.451786Z",
  "result": "observed",
  "start_time": "2019-07-06T08:27:20.451642Z",
  "sub_frames": [],
  "success": true
}

要运行它

它将取路径自 env、EVENTS_PATH(强制)和 LOG_DIR(如果不存在,则取 /var/log/)

EVENTS_PATH="" LOG_DIR="" cargo run --bin main

依赖项

~8–16MB
~221K SLoC