{"id":26479379,"url":"https://github.com/fastn-stack/observer","last_synced_at":"2025-12-12T14:32:21.334Z","repository":{"id":52439150,"uuid":"164217942","full_name":"fastn-stack/observer","owner":"fastn-stack","description":"Observability for rust servers","archived":false,"fork":false,"pushed_at":"2022-12-18T11:27:10.000Z","size":139,"stargazers_count":1,"open_issues_count":2,"forks_count":1,"subscribers_count":39,"default_branch":"master","last_synced_at":"2025-03-15T07:04:50.844Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.amitu.com/observer/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fastn-stack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-05T13:35:39.000Z","updated_at":"2024-09-26T15:21:37.000Z","dependencies_parsed_at":"2023-01-29T19:00:51.353Z","dependency_job_id":null,"html_url":"https://github.com/fastn-stack/observer","commit_stats":null,"previous_names":["ackotech/observer","fifthtry/observer"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastn-stack%2Fobserver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastn-stack%2Fobserver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastn-stack%2Fobserver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastn-stack%2Fobserver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fastn-stack","download_url":"https://codeload.github.com/fastn-stack/observer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244535020,"owners_count":20468150,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-20T01:41:27.579Z","updated_at":"2025-12-12T14:32:21.296Z","avatar_url":"https://github.com/fastn-stack.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Observer\n\n** Observer is a library to capture observability of rust servers\n\n## Observer 2.0 in action\nTo use observer 2.0\n1. Add dependency into `Cargo.toml`.\n```toml\nobserver = \"0.2.0\"\nobserver_attribute = \"0.1.6\"\n```\n2. import these dependency into `lib.rs`.\n```rust\nextern crate observer;\n#[macro_use]\nextern crate observer_attribute;\n```\n3. Define event json file and export events path as EVENTS_PATH.\n```shell script\nexport EVENTS_PATH=\"\u003cPath of events.json file\u003e\"\n```\n\nevent.json file\n\n```json\n{\n    \"foo__create_temp\" : {\n      \"critical\" : true,\n      \"result_type\": \"list\",\n      \"fields\" : {\n        \"id\" : \"string\"\n      }\n    },\n    \"update_temp\" : {\n      \"critical\" : false,\n      \"result_type\": \"i32\",\n      \"fields\" : {\n        \"id\" : \"string\"\n      }\n    }\n}\n```\n\n4. Use observer into your project\n\n```rust\n// Here namespace and with_result are optional parameter.\n// If define namespace so in event file needs to define `foo__create_policy`\n// If with_result defined so it log error also, if function returns an error.\nuse observer::prelude::*;\nuse observer::Result;\n\npub struct Temp;\n\n#[observed(with_result)]\npub fn update_temp(id: \u0026str) -\u003e observer::Result\u003cTemp\u003e {\n    observe_field(\"id\", id); // Need to tell type of id's value in event.json\n    observe_result(2314);  // Need to tell type of result in event.json\n    observer::observe_span_log(\"Message from update temp\");\n    Ok(Temp)\n}\n\n#[observed(namespace = \"foo\")]\npub fn create_temp(id: \u0026str) -\u003e observer::Result\u003cTemp\u003e {\n    observe_field(\"id\", \"4839\");\n    observe_result(\u0026vec![1,2,3,4]);\n    update_temp(id)\n}\n\nfn main(){\n    // define logger\n    let logger = observer::backends::logger::Logger::builder()\n            .with_path(\"/tmp/observer.log\")\n            .with_stdout()\n            .build();\n\n    // Initialize observer with logger\n    observer::builder(Box::new(logger))\n            .create_context(\"main\")\n            .init();\n\n    // Call your functions\n    let _result = create_temp(\"temp\");\n\n    // End of the observer.\n    observer::end_context();\n\n}\n```\n\nIn `stdout` it should look like\n```text\nlogger_initialized\ncontext: main [0ms, 2020-01-29 11:10:54.728594 UTC]\n    foo__create_temp: 0ms\n        @id: \"4839\"\n        @@success: true\n        #result: [1,2,3,4]\n        update_temp: 0ms\n            @id: \"temp\"\n            @@success: true\n            #result: 2314\n            logs:\n               - 0ms: Message from update temp\n```\n\nIn log file it should look the same.\n```text\nlogger_initialized\ncontext: main [0ms, 2020-01-29 11:10:54.728594 UTC]\n    foo__create_temp: 0ms\n        @id: \"4839\"\n        @@success: true\n        #result: [1,2,3,4]\n        update_temp: 0ms\n            @id: \"temp\"\n            @@success: true\n            #result: 2314\n            logs:\n               - 0ms: Message from update temp\n```\n\n\n## Observer `0.1.*` in action\nTo use Observer\n1. Have to define events file(json file and mandatory).\n2. Have to define logs dir else it will take default as `/var/log/`.\n\nFirstly We have to define an events to observe functions. Here Events are nothing\nbut same as function name and in events we have tell which fields has be save in breadcrumbs.\nAnd critical means whether to save this function locally or queue. If critical\nIt will go directly to queue else Observer will save it local.\n\nHere we have defined to events `observer_me` and  `observe_me_too` (same as function name).\n\n```json\n{\n    \"observe_me\" : {\n        \"critical\" : true,\n        \"fields\" : {\n            \"foo\" : \"String\",\n            \"foo1\" : \"f32\"\n        }\n    },\n    \"observe_me_too\" : {\n        \"critical\" : false,\n        \"fields\" : {\n            \"foo1\" : \"i32\"\n        }\n    }\n}\n```\n\n\n```rust\n// src/bin/main.rs\nuse observer::{\n    context::{observe_string, observe_i32, observe_f32},\n    observe::observe,\n    queue::Queue,\n};\n\n#[observed] // Need to define only this on top of fn which we want to observe\n// Context reference is mandatory to pass in observer function.\n// fn should be return Result type.\nfn observe_me(ctx: \u0026Context, other_params: i32)-\u003e Result\u003cString\u003e {\n    // in \"foo\" can store only string value else it will give compile error\n    // It will this in breadcrumbs in Frame\n    observe_field(\"foo\", \"value\".to_string());\n    // in \"foo1\" can store only f32 value else it will give compile error\n    // It will this in breadcrumbs in Frame\n    observe_field(\"foo1\", 32.02);\n    some_other_fn_call();\n\n    // Observing this fn also and it will become a sub-frame of observe_me\n    observe_me_too(ctx);\n    Ok(\"observed\")\n}\n\nfn some_other_fn_call() {}\n\n#[observed]\nfn observe_me_too(ctx: \u0026Context) -\u003e Result\u003ci32\u003e {\n    observe_field(\"foo1\", 32);\n    Ok(12)\n}\n\n\n#[derive(Serialize, Debug, Deserialize)]\npub struct DemoQueue {\n    pub name: String,\n}\n\n#[typetag::serde(name = \"Abc\")]\nimpl Queue for DemoQueue {\n    // TODO: Will give complete definition of in next version surely\n    fn enqueue(\u0026mut self, data: serde_json::Value) {\n        println!(\"Data: {}\", data)\n    }\n}\n\n\nfn main() {\n    let ctx = Context::new(\n        \"test_context\".to_string(),\n        Box::new(DemoQueue{name: \"Abrar\".to_string()})\n    );\n    let _result = observe_me(\u0026ctx, 12);\n    ctx.finalise();\n}\n```\n\nWe are calling observer_me as first function and observer_me_too inside it.\nIn case of Context Object Observer will create frame observer_me and observer_me_too.\nBecause observer_me_too is calling calling inside from observer_me so it will become\nsub-frame of observer_me.  \n\nIt will create log dir by given path or default(/var/log/) and save context into\nlog_dir_path/context and events in log_dir_path/observe_me and log_dir_path/observe_me_too\nbased of criticality of of an event.\n\n## Context log will look like this\n```json\n{\n  \"frame\": {\n    \"breadcrumbs\": {},\n    \"key\": \"17eb437f-a5e2-4243-8dac-fa636429dcf9\",\n    \"result\": null,\n    \"sub_frames\": [\n      {\n        \"breadcrumbs\": {\n          \"foo\": 32\n        },\n        \"key\": \"59471fc8-3391-4619-b341-931658a2296e\",\n        \"result\": 12,\n        \"sub_frames\": [\n          {\n            \"breadcrumbs\": {\n              \"foo1\": 32.02\n            },\n            \"key\": \"399c8d43-16fb-4cd3-8273-b2666026f2f0\",\n            \"result\": \"observed\",\n            \"sub_frames\": [],\n            \"success\": true,\n            \"end_time\": \"2019-07-06T08:27:20.451786Z\",\n            \"id\": \"observe_me_too\",\n            \"start_time\": \"2019-07-06T08:27:20.451642Z\"\n          }\n        ],\n        \"success\": true,\n        \"end_time\": \"2019-07-06T08:27:20.452680Z\",\n        \"id\": \"observe_me\",\n        \"start_time\": \"2019-07-06T08:27:20.451618Z\"\n      }\n    ],\n    \"success\": null,\n    \"end_time\": \"2019-07-06T08:27:20.452683Z\",\n    \"id\": \"main\",\n    \"start_time\": \"2019-07-06T08:27:20.451590Z\"\n  },\n  \"key\": \"302a5760-107a-4826-8670-2efd57db27c2\",\n  \"queue\": {\n    \"type\": \"Abc\",\n    \"value\": {\n      \"name\": \"Abrar\"\n    }\n  },\n  \"id\": \"test_context\"\n}\n```\n\n## Frame/Event log will look like this\n```json\n{\n  \"key\": \"399c8d43-16fb-4cd3-8273-b2666026f2f0\",\n  \"id\": \"observe_me_too\",\n  \"breadcrumbs\": {\n    \"foo1\": 32.02\n  },\n  \"end_time\": \"2019-07-06T08:27:20.451786Z\",\n  \"result\": \"observed\",\n  \"start_time\": \"2019-07-06T08:27:20.451642Z\",\n  \"sub_frames\": [],\n  \"success\": true\n}\n```\n\n#### TO run it\nIt will take to path from env,\nEVENTS_PATH(Mandatory) and LOG_DIR (If not exists so it will take /var/log/)\n```bash\nEVENTS_PATH=\"\" LOG_DIR=\"\" cargo run --bin main\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffastn-stack%2Fobserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffastn-stack%2Fobserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffastn-stack%2Fobserver/lists"}