{"id":48366999,"url":"https://github.com/brickfrog/moontrace","last_synced_at":"2026-04-12T23:27:01.890Z","repository":{"id":346568998,"uuid":"1189140761","full_name":"brickfrog/moontrace","owner":"brickfrog","description":"Structured tracing for MoonBit — spans, structured fields, pluggable subscribers","archived":false,"fork":false,"pushed_at":"2026-03-25T09:59:46.000Z","size":127,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-25T15:44:45.897Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"MoonBit","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brickfrog.png","metadata":{"files":{"readme":"README.mbt.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":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-23T02:38:30.000Z","updated_at":"2026-03-25T09:59:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/brickfrog/moontrace","commit_stats":null,"previous_names":["brickfrog/moontrace"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/brickfrog/moontrace","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickfrog%2Fmoontrace","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickfrog%2Fmoontrace/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickfrog%2Fmoontrace/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickfrog%2Fmoontrace/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brickfrog","download_url":"https://codeload.github.com/brickfrog/moontrace/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickfrog%2Fmoontrace/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31439442,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T13:13:19.330Z","status":"ssl_error","status_checked_at":"2026-04-05T13:13:17.778Z","response_time":75,"last_error":"SSL_read: 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":[],"created_at":"2026-04-05T15:01:24.275Z","updated_at":"2026-04-05T15:01:24.761Z","avatar_url":"https://github.com/brickfrog.png","language":"MoonBit","funding_links":[],"categories":[],"sub_categories":[],"readme":"# brickfrog/moontrace\n\nStructured tracing for MoonBit. Spans, structured fields, pluggable subscribers.\n\nInspired by Rust's [tracing](https://github.com/tokio-rs/tracing) crate and [loguru](https://github.com/Delgan/loguru)'s developer experience.\n\n## Install\n\n```\nmoon add brickfrog/moontrace\n```\n\n## Quick Start\n\n```moonbit\nfn main {\n  // One-liner setup with colored console output\n  @console.initialize()\n\n  // Structured events\n  @moontrace.info(\"server started\", fields=[@moontrace.field(\"port\", 8080)])\n  @moontrace.warn(\"slow query\", fields=[@moontrace.field(\"ms\", 250)])\n\n  // Spans with duration tracking\n  @moontrace.with_span(\"handle_request\", fn() {\n    @moontrace.info(\"processing\")\n  })\n}\n```\n\nOutput:\n\n```\n14:31:43.903 | INFO  | myapp — server started  port=8080\n14:31:43.904 | WARN  | myapp — slow query  ms=250\n14:31:43.904 | TRACE | moontrace — span.enter handle_request\n14:31:43.904 | INFO  | myapp — processing\n14:31:43.904 | TRACE | moontrace — span.exit handle_request  duration_ns=12345\n```\n\n## Events\n\nFire-and-forget structured log entries at five levels:\n\n```moonbit\n@moontrace.trace(\"verbose detail\")\n@moontrace.debug(\"debugging info\")\n@moontrace.info(\"normal operation\")\n@moontrace.warn(\"something unexpected\")\n@moontrace.error(\"something broke\")\n```\n\nAttach structured fields to any event:\n\n```moonbit\n@moontrace.info(\"request handled\",\n  fields=[\n    @moontrace.field(\"method\", \"GET\"),\n    @moontrace.field(\"path\", \"/api/users\"),\n    @moontrace.field(\"status\", 200),\n  ])\n```\n\nThe `field()` function uses MoonBit's `ToJson` trait dispatch — any type implementing `ToJson` works. For raw `Json` values, use `fields()` instead.\n\nEvery event automatically captures its source package via `SourceLoc`, available as `event.source`.\n\n## Global Context\n\nSet fields once, automatically included in every event:\n\n```moonbit\n@moontrace.set_global_field(\"service\", \"my-app\")\n@moontrace.set_global_field(\"version\", \"1.2.0\")\n\n@moontrace.info(\"started\")  // automatically includes service and version fields\n```\n\nExplicit fields override global context on key collision.\n\n```moonbit\n@moontrace.remove_global_field(\"version\")  // remove a single field\n@moontrace.clear_global_fields()            // remove all\n```\n\n## Per-Module Filtering\n\nFilter log levels by package:\n\n```moonbit\n@moontrace.set_module_filter(\"my/db/package\", @moontrace.Debug)\n@moontrace.set_module_filter(\"my/http/package\", @moontrace.Warn)\n```\n\nModule names are extracted automatically from `SourceLoc` at each call site. No manual tagging needed.\n\n## Spans\n\nSpans track operations with enter/exit lifecycle, duration, and distributed tracing IDs:\n\n```moonbit\nlet s = @moontrace.span(\"db_query\")\n  .with_field(\"table\", \"users\")\n  .with_field(\"limit\", 100)\ns.enter()\n// ... do work ...\ns.record(\"rows\", 42)  // add fields after creation\ns.exit()\n// s.duration() returns elapsed nanoseconds\n```\n\nEvery span gets auto-generated `trace_id` (32 hex chars) and `span_id` (16 hex chars).\n\n### Error Handling on Spans\n\n```moonbit\n@moontrace.with_span_ctx(\"db_query\", fn(s) {\n  match run_query() {\n    Ok(result) =\u003e s.set_status(@moontrace.Ok)\n    Err(e) =\u003e s.record_error(e.to_string())  // sets SpanError + records error field\n  }\n})\n```\n\n`record_error` sets the span status to `SpanError`, records the error message as a field, and includes both in the span's exit event and JSON output.\n\n### Convenience Wrappers\n\n```moonbit\n// Auto enter/exit\n@moontrace.with_span(\"operation\", fn() {\n  @moontrace.info(\"inside span\")\n})\n\n// Access the span inside the closure\n@moontrace.with_span_ctx(\"operation\", fn(span) {\n  span.record(\"result\", \"ok\")\n})\n\n// Nested spans with parent linking\n@moontrace.with_span_ctx(\"parent_op\", fn(parent) {\n  @moontrace.with_child_span(parent, \"child_op\", fn(_child) {\n    @moontrace.info(\"in child span\")\n  })\n})\n```\n\n`with_child_span` propagates the parent's `trace_id` and sets `parent_span_id`, linking spans in a trace.\n\n### Cross-Process Trace Correlation\n\nFor distributed tracing across process boundaries, inject an existing trace ID:\n\n```moonbit\nlet s = @moontrace.span_with_trace(\"handle_request\", external_trace_id)\n```\n\n### Async Span Propagation\n\nMoonBit has no task-local storage, so spans must be passed explicitly across async boundaries:\n\n```moonbit\n@moontrace.with_span_ctx(\"request\", fn(parent) {\n  async_work(parent)\n})\n\npub async fn async_work(parent : @moontrace.Span) -\u003e Unit {\n  @moontrace.with_child_span(parent, \"async_step\", fn(_s) {\n    // child inherits parent's trace_id\n  })\n}\n```\n\n## Subscribers\n\nSubscribers receive events. Set one globally:\n\n```moonbit\n@moontrace.set_subscriber(fn(event) {\n  println(event.format())\n})\n```\n\n### Console Subscriber\n\nHuman-readable colored output with timestamps, source, and pipe separators:\n\n```moonbit\n@console.initialize()\n// or with options:\n@moontrace.set_subscriber(@console.subscriber(\n  min_level=@moontrace.Info,\n  color=true,\n))\n```\n\nOutput:\n\n```\n14:31:43.903 | INFO  | server — UDS server listening  path=\".choir/server.sock\"\n14:31:44.012 | WARN  | poller — retry  attempt=3 max=5\n14:31:44.500 | ERROR | handler — delivery failed  target=\"leaf-1\" exit_code=2\n```\n\n### JSON Subscriber\n\nMachine-readable JSON output:\n\n```moonbit\n@json.initialize()\n// or with options:\n@moontrace.set_subscriber(@json.subscriber(min_level=@moontrace.Warn))\n```\n\n### OTLP Export\n\nConvert events and spans to OpenTelemetry-compatible JSON:\n\n```moonbit\nlet exp = @otlp.exporter(\n  log_output=fn(json) { send_to_collector(json) },\n  span_output=fn(json) { send_to_collector(json) },\n  capacity=100,\n)\n@moontrace.set_subscriber(exp.subscriber())\n\n// Spans must be added manually\nlet s = @moontrace.span(\"operation\")\ns.enter()\n// ... work ...\ns.exit()\nexp.add_span(@otlp.span_to_otlp(s))\nexp.flush()  // send remaining batched data\n```\n\nThe OTLP package handles format conversion (events to log records, spans to OTLP spans with proper severity codes and attributes). You provide the transport.\n\n### Subscriber Composition\n\nRoute events to multiple subscribers:\n\n```moonbit\n@moontrace.set_subscriber(@moontrace.compose([\n  @console.subscriber(min_level=@moontrace.Info),\n  @json.subscriber(min_level=@moontrace.Debug),\n]))\n```\n\nFilter events for any subscriber:\n\n```moonbit\n@moontrace.set_subscriber(\n  @moontrace.with_filter(@json.subscriber(), @moontrace.Warn)\n)\n```\n\n### Utility Subscribers\n\n```moonbit\n// No-op (for benchmarks/testing)\n@moontrace.set_subscriber(@moontrace.noop())\n\n// Intercept (run a side-effect before the main subscriber)\n@moontrace.set_subscriber(@moontrace.intercept(\n  main_subscriber,\n  fn(e) { metrics.increment(e.level.to_string()) },\n))\n```\n\n### Buffered Subscriber\n\nBatch events and flush on demand or at capacity:\n\n```moonbit\nlet buf = @moontrace.buffer(@json.subscriber(), capacity=100)\n@moontrace.set_subscriber(buf.subscriber())\n// ... events are batched ...\nbuf.flush()  // send all buffered events\n```\n\n## Performance\n\nSet a global minimum level to skip event allocation entirely:\n\n```moonbit\n@moontrace.set_min_level(@moontrace.Info)\n// trace() and debug() calls now short-circuit before allocating Event\n```\n\nGlobal context fields use a fast path — zero allocation overhead when no context is set.\n\n## Serialization\n\nEvents, spans, and fields all support JSON serialization:\n\n```moonbit\nlet event_json = event.to_json().stringify()\nlet span_json = span.to_json().stringify()\n```\n\nSpan JSON includes trace_id, span_id, parent_span_id, status, and duration.\n\nHuman-readable formatting with the new pipe-separated style:\n\n```moonbit\nlet plain = event.format()           // no color\nlet colored = event.format(color=true) // ANSI colored\n```\n\nAll types implement `Show` for `println` and string interpolation:\n\n```moonbit\nprintln(event)       // human-readable output\nlet s = \"\\{span}\"    // string interpolation works\n```\n\n## Architecture\n\n```\n@moontrace           # core — what libraries depend on\n@moontrace/json      # JSON subscriber (structured output)\n@moontrace/console   # console subscriber (colored, human-readable)\n@moontrace/otlp      # OpenTelemetry JSON format conversion\n```\n\nLibraries instrument with `@moontrace`. Applications choose subscribers.\n\n## Contributing\n\nContributions welcome! Please:\n\n1. `moon fmt` before committing\n2. `moon test --target native` must pass\n3. Run `moon info \u0026\u0026 moon fmt` if you change public APIs (updates `.mbti` files)\n4. Add tests for new features\n\nThe pre-commit hook runs `moon fmt` and `moon check` automatically.\n\n### Development\n\n```sh\ngit clone https://github.com/brickfrog/moontrace\ncd moontrace\ngit config core.hooksPath .githooks\nmoon test --target native\n```\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrickfrog%2Fmoontrace","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrickfrog%2Fmoontrace","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrickfrog%2Fmoontrace/lists"}