{"id":49901453,"url":"https://github.com/joswayski/sjl","last_synced_at":"2026-05-16T06:37:07.426Z","repository":{"id":321199387,"uuid":"1084827036","full_name":"joswayski/sjl","owner":"joswayski","description":"Simple JSON Logger with proper enum and nested JSON support (no escaped strings!)","archived":false,"fork":false,"pushed_at":"2026-05-13T08:07:37.000Z","size":134,"stargazers_count":7,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-13T10:10:59.543Z","etag":null,"topics":["json","logger","rust","simple","structured"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/sjl","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joswayski.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-28T08:06:33.000Z","updated_at":"2025-11-30T09:28:55.000Z","dependencies_parsed_at":"2025-10-28T12:07:33.524Z","dependency_job_id":"4e111991-4e05-43f3-822e-859800012581","html_url":"https://github.com/joswayski/sjl","commit_stats":null,"previous_names":["joswayski/sjl"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/joswayski/sjl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joswayski%2Fsjl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joswayski%2Fsjl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joswayski%2Fsjl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joswayski%2Fsjl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joswayski","download_url":"https://codeload.github.com/joswayski/sjl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joswayski%2Fsjl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33092737,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-16T04:41:52.686Z","status":"ssl_error","status_checked_at":"2026-05-16T04:41:52.009Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["json","logger","rust","simple","structured"],"created_at":"2026-05-16T06:37:06.798Z","updated_at":"2026-05-16T06:37:07.408Z","avatar_url":"https://github.com/joswayski.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sjl - Simple JSON Logger\n\n 📦 **[crates.io](https://crates.io/crates/sjl)** | 📚 **[docs.rs](https://docs.rs/sjl)**\n\n### Why?\nThe most popular logging crate, [tracing](https://crates.io/crates/tracing), has [problems with nested JSON](https://www.reddit.com/r/rust/comments/1k75jvc/how_can_i_emit_a_tracing_event_with_an_unescaped/) unless you use the `valuable` crate with it which is [unstable and behind a feature flag for 3 years](https://github.com/tokio-rs/tracing/discussions/1906)... but that [still has issues with enums](https://github.com/tokio-rs/tracing/issues/3051) and doesn't feel natural to use with `.as_value()` everywhere.  The [slog](https://crates.io/crates/slog) crate has similar issues—I've written about both [here](https://josevalerio.com/rust-json-logging).\n\nIf you just want a simple JSON logger, you might find this useful.\n\n## Features\n- Batched, non-blocking writes\n- Graceful shutdown (flushes on exit)\n- Falls back to sync writes if buffer is full\n- Customizable colors, timestamps, batch sizes\n- Pretty-printing mode for development\n- Works with any `Serialize` type\n- Macros! `debug!()`, `info!()`, `warn!()`, and `error!()`\n- Global context fields that appear in every log message\n\n\n ## Installation\n\n ```bash\n cargo add sjl\n ```\n\n## Usage\n```rust\nuse sjl::{debug, error, info, warn, LogLevel, Logger, RGB};\nuse serde::Serialize;\nuse serde_json::json;\n\n#[derive(Serialize)] // This is all you need\nstruct User {\n    id: u64,\n    name: String,\n}\n\n#[derive(Serialize)]\nenum OrderStatus {\n    Pending,\n    Shipped { tracking_number: String },\n    Delivered,\n}\n\n#[derive(Serialize)]\nstruct Order {\n    user: User,\n    items: Vec\u003cOrderItem\u003e,\n}\n\n#[derive(Serialize)]\nstruct OrderItem {\n    name: String,\n    price: f64,\n    quantity: u32,\n    status: OrderStatus,\n}\n\nfn main() {\n    // Initialize once at startup\n    Logger::init()\n        // Optional config\n        .min_level(LogLevel::Debug)       // Minimum log level (default: Debug)\n        .batch_size(100)                  // Logs per batch (default: 50)\n        .batch_duration_ms(100)           // Max ms before flush (default: 50)\n        .buffer_size(5000)                // Channel capacity (default: 1024)\n        .timestamp_format(\"%Y-%m-%dT%H:%M:%S%.3fZ\")  // ISO 8601 (default)\n        .timestamp_key(\"tz\")              // Rename this field if you want (default: timestamp)\n        .pretty(true)                     // Pretty-print JSON (default: false)\n        .debug_color(RGB::new(38, 45, 56))   // Customize colors\n        .info_color(RGB::new(15, 115, 255))\n        .warn_color(RGB::new(247, 155, 35))\n        .error_color(RGB::new(255, 0, 0))\n        // Context fields appear in EVERY log message at the top level\n        .context(\"environment\", \"production\")\n        .context(\"service\", \"order-api\")\n        .context(\"metadata\", json!({\n            \"instance_id\": \"i-1234567890abcdef0\",\n            \"pod_name\": \"order-api-7d4f8c9b5-x8k2p\",\n            \"git_sha\": \"abc123f\"\n        }))\n        // Call this at the end\n        .build(); \n\n    // Strings\n    debug!(\"App started\");\n    info!(\"Server listening\", \"0.0.0.0:8080\");\n\n    // Structs\n    info!(User { id: 1, name: \"Alice\".into() });\n    info!(\"User authenticated\", User { id: 1, name: \"Alice\".into() });\n\n    // Enums (serialize correctly!)\n    warn!(OrderStatus::Pending);\n    warn!(OrderStatus::Shipped { tracking_number: \"1Z999AA10123456784\".into() });\n\n    // Ad-hoc JSON\n    error!(json!({\n        \"error\": \"connection_failed\",\n        \"host\": \"db.example.com\"\n    }));\n\n    // Complex: Vec of structs with enums\n    info!(\"Order processed\", Order {\n        user: User { id: 42, name: \"John\".into() },\n        items: vec![\n            OrderItem {\n                name: \"Widget\".into(),\n                price: 29.99,\n                quantity: 2,\n                status: OrderStatus::Shipped { tracking_number: \"1Z999AA10123456784\".into() },\n            },\n            OrderItem {\n                name: \"Gadget\".into(),\n                price: 49.99,\n                quantity: 1,\n                status: OrderStatus::Pending,\n            },\n        ],\n    });\n}\n```\n\n### Pretty Mode\n\nWhen `.pretty(true)` is enabled, logs are formatted with indentation and newlines for easier reading during development:\n\n```json\n{\n  \"data\": {\n    \"items\": [\n      {\n        \"name\": \"Widget\",\n        \"price\": 29.99,\n        \"quantity\": 2,\n        \"status\": {\n          \"Shipped\": {\n            \"tracking_number\": \"1Z999AA10123456784\"\n          }\n        }\n      },\n      {\n        \"name\": \"Gadget\",\n        \"price\": 49.99,\n        \"quantity\": 1,\n        \"status\": \"Pending\"\n      }\n    ],\n    \"user\": {\n      \"id\": 42,\n      \"name\": \"John\"\n    }\n  },\n  \"environment\": \"production\",\n  \"level\": \"INFO\",\n  \"message\": \"Order processed\",\n  \"metadata\": {\n    \"git_sha\": \"abc123f\",\n    \"instance_id\": \"i-1234567890abcdef0\",\n    \"pod_name\": \"order-api-7d4f8c9b5-x8k2p\"\n  },\n  \"service\": \"order-api\",\n  \"tz\": \"2025-10-31T07:33:05.627Z\"\n}\n```\n\n\n\n### Compact Mode (Default)\n\nWith `.pretty(false)` or omitted (default), logs are output as single-line JSON:\n\n```json\n{\"level\":\"DEBUG\",\"tz\":\"2025-10-31T07:33:44.333Z\",\"message\":\"App started\",\"environment\": \"production\", \"service\": \"order-api\", \"metadata\": {\"git_sha\":\"abc123f\",\"instance_id\":\"i-1234567890abcdef0\",\"pod_name\":\"order-api-7d4f8c9b5-x8k2p\"}}\n{\"level\":\"INFO\",\"tz\":\"2025-10-31T07:33:44.333Z\",\"message\":\"Server listening\",\"data\":\"0.0.0.0:8080\",\"environment\": \"production\", \"service\": \"order-api\", \"metadata\": {\"git_sha\":\"abc123f\",\"instance_id\":\"i-1234567890abcdef0\",\"pod_name\":\"order-api-7d4f8c9b5-x8k2p\"}}\n{\"level\":\"INFO\",\"tz\":\"2025-10-31T07:33:44.333Z\",\"data\":{\"id\":1,\"name\":\"Alice\"},\"environment\": \"production\", \"service\": \"order-api\", \"metadata\": {\"git_sha\":\"abc123f\",\"instance_id\":\"i-1234567890abcdef0\",\"pod_name\":\"order-api-7d4f8c9b5-x8k2p\"}}\n{\"level\":\"INFO\",\"tz\":\"2025-10-31T07:33:44.333Z\",\"message\":\"User authenticated\",\"data\":{\"id\":1,\"name\":\"Alice\"},\"environment\": \"production\", \"service\": \"order-api\", \"metadata\": {\"git_sha\":\"abc123f\",\"instance_id\":\"i-1234567890abcdef0\",\"pod_name\":\"order-api-7d4f8c9b5-x8k2p\"}}\n{\"level\":\"WARN\",\"tz\":\"2025-10-31T07:33:44.333Z\",\"message\":\"Pending\",\"environment\": \"production\", \"service\": \"order-api\", \"metadata\": {\"git_sha\":\"abc123f\",\"instance_id\":\"i-1234567890abcdef0\",\"pod_name\":\"order-api-7d4f8c9b5-x8k2p\"}}\n{\"level\":\"WARN\",\"tz\":\"2025-10-31T07:33:44.333Z\",\"data\":{\"Shipped\":{\"tracking_number\":\"1Z999AA10123456784\"}},\"environment\": \"production\", \"service\": \"order-api\", \"metadata\": {\"git_sha\":\"abc123f\",\"instance_id\":\"i-1234567890abcdef0\",\"pod_name\":\"order-api-7d4f8c9b5-x8k2p\"}}\n{\"level\":\"ERROR\",\"tz\":\"2025-10-31T07:33:44.333Z\",\"data\":{\"error\":\"connection_failed\",\"host\":\"db.example.com\"},\"environment\": \"production\", \"service\": \"order-api\", \"metadata\": {\"git_sha\":\"abc123f\",\"instance_id\":\"i-1234567890abcdef0\",\"pod_name\":\"order-api-7d4f8c9b5-x8k2p\"}}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoswayski%2Fsjl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoswayski%2Fsjl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoswayski%2Fsjl/lists"}