Add service infrastructure for long-running deployment
- Add configuration system (config/*.exs, OrgGarden.Config) - Refactor supervision tree with DynamicSupervisor and Registry - Add OrgGarden.Server for serve mode lifecycle management - Add health check HTTP endpoints (Bandit/Plug on :9090) - Add telemetry events for export and watcher operations - Implement graceful shutdown with SIGTERM handling - Add Mix Release support with overlay scripts - Add NixOS module for systemd service deployment - Update documentation with service usage
This commit is contained in:
109
lib/org_garden/telemetry.ex
Normal file
109
lib/org_garden/telemetry.ex
Normal file
@@ -0,0 +1,109 @@
|
||||
defmodule OrgGarden.Telemetry do
|
||||
@moduledoc """
|
||||
Telemetry event definitions and logging handler.
|
||||
|
||||
## Events
|
||||
|
||||
The following telemetry events are emitted:
|
||||
|
||||
* `[:org_garden, :export, :start]` — Export of a single file started
|
||||
- Metadata: `%{file: path}`
|
||||
|
||||
* `[:org_garden, :export, :stop]` — Export of a single file completed
|
||||
- Measurements: `%{duration: native_time}`
|
||||
- Metadata: `%{file: path}`
|
||||
|
||||
* `[:org_garden, :export, :exception]` — Export failed
|
||||
- Measurements: `%{duration: native_time}`
|
||||
- Metadata: `%{file: path, kind: kind, reason: reason}`
|
||||
|
||||
* `[:org_garden, :watcher, :file_processed]` — File change processed
|
||||
- Metadata: `%{path: path, event: :created | :modified | :deleted}`
|
||||
|
||||
* `[:org_garden, :server, :start]` — Server started
|
||||
- Metadata: `%{port: port}`
|
||||
|
||||
* `[:org_garden, :server, :stop]` — Server stopped
|
||||
- Metadata: `%{reason: reason}`
|
||||
|
||||
## Usage
|
||||
|
||||
Attach a handler to log events:
|
||||
|
||||
OrgGarden.Telemetry.attach_logger()
|
||||
|
||||
Or use `:telemetry.attach/4` for custom handling.
|
||||
"""
|
||||
|
||||
require Logger
|
||||
|
||||
@doc """
|
||||
Attach a simple logging handler for telemetry events.
|
||||
"""
|
||||
def attach_logger do
|
||||
events = [
|
||||
[:org_garden, :export, :stop],
|
||||
[:org_garden, :export, :exception],
|
||||
[:org_garden, :watcher, :file_processed],
|
||||
[:org_garden, :server, :start],
|
||||
[:org_garden, :server, :stop]
|
||||
]
|
||||
|
||||
:telemetry.attach_many(
|
||||
"org-garden-logger",
|
||||
events,
|
||||
&handle_event/4,
|
||||
nil
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Detach the logging handler.
|
||||
"""
|
||||
def detach_logger do
|
||||
:telemetry.detach("org-garden-logger")
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Event handlers
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
defp handle_event([:org_garden, :export, :stop], measurements, metadata, _config) do
|
||||
duration_ms = System.convert_time_unit(measurements.duration, :native, :millisecond)
|
||||
Logger.debug("Export completed: #{metadata.file} (#{duration_ms}ms)")
|
||||
end
|
||||
|
||||
defp handle_event([:org_garden, :export, :exception], _measurements, metadata, _config) do
|
||||
Logger.error("Export failed: #{metadata.file} - #{inspect(metadata.reason)}")
|
||||
end
|
||||
|
||||
defp handle_event([:org_garden, :watcher, :file_processed], _measurements, metadata, _config) do
|
||||
Logger.debug("Watcher processed: #{metadata.event} #{metadata.path}")
|
||||
end
|
||||
|
||||
defp handle_event([:org_garden, :server, :start], _measurements, metadata, _config) do
|
||||
Logger.info("Server started on port #{metadata.port}")
|
||||
end
|
||||
|
||||
defp handle_event([:org_garden, :server, :stop], _measurements, metadata, _config) do
|
||||
Logger.info("Server stopped: #{inspect(metadata.reason)}")
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Convenience functions for emitting events
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
@doc """
|
||||
Wrap a function with export telemetry events.
|
||||
"""
|
||||
def span_export(file, fun) when is_function(fun, 0) do
|
||||
:telemetry.span(
|
||||
[:org_garden, :export],
|
||||
%{file: file},
|
||||
fn ->
|
||||
result = fun.()
|
||||
{result, %{file: file}}
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user