Documentation
¶
Overview ¶
Package auditlog provides an audit-log plugin for samber/do v2 that tracks every service registration, invocation, shutdown, and health check with timestamps, dependency graph inference, build duration measurement, and provider type tracking.
Each Event carries a ServiceType (ProviderType) identifying the provider kind (lazy, eager, transient, alias). ServiceInfo includes IsHealthchecker and IsShutdowner capabilities detected via do.ExplainInjector.
Config.Validate() checks configuration constraints. Export formats include JSON reports, NDJSON event streams, CSV/TSV, self-contained HTML, Mermaid, PlantUML, Graphviz DOT, and D2 diagrams.
templ: version: v0.3.1020
Example (Validate) ¶
package main
import (
"fmt"
auditlog "github.com/larsartmann/samber-do-auditlog"
)
func main() {
cfg := auditlog.Config{ContainerID: "my-app"}
fmt.Println("valid:", cfg.Validate())
cfg = auditlog.Config{ContainerID: "my/app"}
fmt.Println("invalid:", cfg.Validate() != nil)
}
Output: valid: <nil> invalid: true
Index ¶
- Constants
- Variables
- func CompareServiceRefs(a, b ServiceRef) int
- func DefaultTableOpts() output.RenderOptions
- func JSONSchema() string
- func LoadReport(path string, opts ...LoadOption) (Report, Format, error)
- func LoadReportFromBytes(data []byte, format Format) (Report, Format, error)
- func LoadReportFromReader(reader io.Reader, format Format) (Report, Format, error)
- func RedriveReportStatuses(report *Report)
- type Config
- type DiffResult
- type Event
- type EventMeta
- type EventType
- type Format
- type LoadOption
- type Phase
- type Plugin
- func (p *Plugin) DroppedEventCount() int64
- func (p *Plugin) Events() []Event
- func (p *Plugin) EventsCount() int
- func (p *Plugin) ExportEventsToNDJSON(path string) error
- func (p *Plugin) ExportFilteredToFile(path string, opts ...ReportOption) error
- func (p *Plugin) ExportToCSV(path string) error
- func (p *Plugin) ExportToD2(path string) error
- func (p *Plugin) ExportToDOT(path string) error
- func (p *Plugin) ExportToFile(path string) error
- func (p *Plugin) ExportToHTML(path string) error
- func (p *Plugin) ExportToHTMLTree(path string) error
- func (p *Plugin) ExportToMermaid(path string) error
- func (p *Plugin) ExportToPlantUML(path string) error
- func (p *Plugin) ExportToTSV(path string) error
- func (p *Plugin) ExportToTable(path string, format output.Format, opts output.RenderOptions) error
- func (p *Plugin) ExportToTree(path string) error
- func (p *Plugin) Opts() *do.InjectorOpts
- func (p *Plugin) RecordHealthCheck(injector do.Injector) map[string]error
- func (p *Plugin) RecordHealthCheckWithContext(ctx context.Context, injector do.Injector) map[string]error
- func (p *Plugin) Report() Report
- func (p *Plugin) ReportFiltered(opts ...ReportOption) Report
- func (p *Plugin) WriteD2(writer io.Writer) error
- func (p *Plugin) WriteDOT(writer io.Writer) error
- func (p *Plugin) WriteEventsNDJSON(writer io.Writer) error
- func (p *Plugin) WriteHTML(writer io.Writer) error
- func (p *Plugin) WriteHTMLTree(writer io.Writer) error
- func (p *Plugin) WriteMermaid(writer io.Writer) error
- func (p *Plugin) WritePlantUML(writer io.Writer) error
- func (p *Plugin) WriteReportCSV(writer io.Writer) error
- func (p *Plugin) WriteReportJSON(writer io.Writer) error
- func (p *Plugin) WriteReportTSV(writer io.Writer) error
- func (p *Plugin) WriteTable(writer io.Writer, format output.Format, opts output.RenderOptions) error
- func (p *Plugin) WriteTree(writer io.Writer) error
- type ProviderMeta
- type ProviderType
- type Recorder
- func (r *Recorder) BuildReport() Report
- func (r *Recorder) DroppedEventCount() int64
- func (r *Recorder) Events() []Event
- func (r *Recorder) EventsCount() int
- func (r *Recorder) OnAfterInvocation(scope *do.Scope, serviceName string, err error)
- func (r *Recorder) OnAfterRegistration(scope *do.Scope, serviceName string)
- func (r *Recorder) OnAfterShutdown(scope *do.Scope, serviceName string, err error)
- func (r *Recorder) OnBeforeInvocation(scope *do.Scope, serviceName string)
- func (r *Recorder) OnBeforeRegistration(scope *do.Scope, serviceName string)
- func (r *Recorder) OnBeforeShutdown(scope *do.Scope, serviceName string)
- func (r *Recorder) RecordHealthCheck(scopeID, scopeName, serviceName string, err error)
- func (r *Recorder) ResolveServiceScope(injector do.Injector, serviceName string) (string, string, bool)
- type Report
- func (r Report) Diff(other Report) DiffResult
- func (r Report) EventsByRef(scopeID, serviceName string) []Event
- func (r Report) EventsByService(serviceName string) []Event
- func (r Report) EventsByType(t EventType) []Event
- func (r Report) FailedServices() []ServiceInfo
- func (r Report) Filtered(opts ...ReportOption) Report
- func (r Report) Index() ReportIndex
- func (r Report) ServiceByName(name string) *ServiceInfo
- func (r Report) ServiceByRef(scopeID, serviceName string) *ServiceInfo
- func (r Report) ServicesByScope(scopeID string) []ServiceInfo
- func (r Report) UnhealthyServices() []ServiceInfo
- func (r Report) Validate() error
- func (r Report) WriteCSV(writer io.Writer) error
- func (r Report) WriteD2(writer io.Writer) error
- func (r Report) WriteDOT(writer io.Writer) error
- func (r Report) WriteHTML(writer io.Writer) error
- func (r Report) WriteHTMLTree(writer io.Writer) error
- func (r Report) WriteHTMLTreeString() (string, error)
- func (r Report) WriteJSON(writer io.Writer) error
- func (r Report) WriteMermaid(writer io.Writer) error
- func (r Report) WriteNDJSON(writer io.Writer) error
- func (r Report) WritePlantUML(writer io.Writer) error
- func (r Report) WriteTSV(writer io.Writer) error
- func (r Report) WriteTable(writer io.Writer, format output.Format, opts output.RenderOptions) error
- func (r Report) WriteTableString(format output.Format, opts output.RenderOptions) (string, error)
- func (r Report) WriteTree(writer io.Writer) error
- func (r Report) WriteTreeString() (string, error)
- type ReportIndex
- type ReportOption
- type ScopeNode
- type ServiceDiff
- type ServiceInfo
- type ServiceRef
- type ServiceStatus
- type StatusMeta
- type TypeMetadata
Examples ¶
Constants ¶
const EnvKeyEnabled = "DO_AUDITLOG_ENABLED"
EnvKeyEnabled is the environment variable that controls audit logging. Set to "true", "1", or "yes" to enable. Any other value (or unset) disables it.
const RootScopeName = "[root]"
RootScopeName is the canonical name for the root scope in samber/do v2.
const SchemaVersion = "0.2.0"
SchemaVersion is the current report schema version.
Variables ¶
var ( ErrEmpty = errors.New("ndjson input is empty") ErrNoEvents = errors.New("ndjson input contains no events") ErrOversizedLine = errors.New("ndjson line exceeds maximum size") )
Errors returned by ReadEvents.
Functions ¶
func CompareServiceRefs ¶ added in v0.1.0
func CompareServiceRefs(a, b ServiceRef) int
CompareServiceRefs is the canonical sort ordering for ServiceRef slices: primary by ServiceName, secondary by ScopeID. Used by report builders and diff output so all ServiceRef lists are consistently ordered.
func DefaultTableOpts ¶ added in v0.3.0
func DefaultTableOpts() output.RenderOptions
DefaultTableOpts returns the default RenderOptions for table export. Convenience for callers who don't need custom rendering options.
func JSONSchema ¶ added in v0.1.0
func JSONSchema() string
JSONSchema returns the canonical JSON Schema (Draft 2020-12) describing the Report export format, derived from this package's public types. Consumers can use it to validate exported report JSON or to generate client code.
func LoadReport ¶ added in v0.1.0
func LoadReport(path string, opts ...LoadOption) (Report, Format, error)
LoadReport reads a report from a file path, auto-detecting the format.
JSON files (single Report object) are loaded via MigrateReport, which handles both v0.1.0 and v0.2.0 schemas. NDJSON files (line-delimited events) are loaded via ReadEvents + ReplayEvents.
Returns the loaded Report, the detected Format, and any error.
func LoadReportFromBytes ¶ added in v0.1.0
LoadReportFromBytes loads a report from raw bytes with format detection.
func LoadReportFromReader ¶ added in v0.1.0
LoadReportFromReader reads a report from an io.Reader. If format is FormatAuto, the entire content is read and the format is detected by inspecting the first non-blank line for a "version" key (JSON Report) or "event_type" key (NDJSON event).
func RedriveReportStatuses ¶ added in v0.2.0
func RedriveReportStatuses(report *Report)
RedriveReportStatuses calls RederiveStatus on every service in the report. Used by MigrateReport (repair) and property-test fixture builders (sanitize).
Types ¶
type Config ¶
type Config struct {
// Enabled turns audit logging on or off. When false the plugin is a no-op.
// If left as zero-value (false), New() checks the DO_AUDITLOG_ENABLED env var.
Enabled bool
// ContainerID is an optional human-readable identifier for the injector.
ContainerID string
// OnEvent is called after each event is captured. Must not block.
// Called sequentially in hook order. Nil disables the callback.
OnEvent func(Event)
// MaxEvents caps the number of events stored in memory. When 0 (default),
// events grow without bound. When > 0, the recorder stops appending new
// events after reaching the cap and increments DroppedEventCount.
// Use this to prevent OOM in long-running processes.
MaxEvents int
// InitialEventCapacity pre-allocates the events slice to avoid runtime
// growslice reallocations. When 0, defaults to 1024. Set this to the
// expected number of events for your workload to eliminate slice growth cost.
InitialEventCapacity int
}
Config controls the audit log plugin behaviour.
type DiffResult ¶ added in v0.0.4
type DiffResult struct {
// AddedServices are services present in `other` but not in `r`.
AddedServices []ServiceRef `json:"added_services,omitempty"`
// RemovedServices are services present in `r` but not in `other`.
RemovedServices []ServiceRef `json:"removed_services,omitempty"`
// ChangedServices are services present in both with different fields.
ChangedServices []ServiceDiff `json:"changed_services,omitempty"`
// EventCountDelta is other.EventCount - r.EventCount.
EventCountDelta int `json:"event_count_delta"`
}
DiffResult describes the differences between two Reports. All slices are nil when empty (no allocation for identical reports).
func (DiffResult) IsEmpty ¶ added in v0.0.4
func (d DiffResult) IsEmpty() bool
IsEmpty returns true when no differences were found.
type Event ¶
type Event struct {
ServiceRef
Sequence int `json:"sequence"`
Timestamp time.Time `json:"timestamp"`
EventType EventType `json:"event_type"`
Phase Phase `json:"phase"`
ServiceType ProviderType `json:"service_type,omitempty"`
ContainerID string `json:"container_id"`
DurationMs *float64 `json:"duration_ms,omitempty"`
Error *string `json:"error,omitempty"`
}
Event is a single, timestamped observation from the DI container lifecycle.
func ReadEvents ¶ added in v0.1.0
ReadEvents reads line-delimited JSON events from reader. Each line must be a single JSON-encoded Event object. Blank lines are skipped. Returns the parsed events in order.
func (Event) IsBefore ¶
IsBefore returns true if the event is the start (before) phase of an operation.
func (Event) IsHealthCheck ¶
IsHealthCheck returns true if the event is a health check event.
func (Event) IsInvocation ¶
IsInvocation returns true if the event is an invocation event.
func (Event) IsRegistration ¶
IsRegistration returns true if the event is a registration event.
func (Event) IsShutdown ¶
IsShutdown returns true if the event is a shutdown event.
type EventType ¶
type EventType string
EventType categorizes audit log events.
func (EventType) Color ¶ added in v0.1.0
Color returns the CSS color token for this event type, used in the HTML visualization.
type Format ¶ added in v0.1.0
type Format int
Format identifies the serialization format of a report file.
type LoadOption ¶ added in v0.1.0
type LoadOption func(*loadConfig)
LoadOption configures LoadReport behavior.
func WithFormat ¶ added in v0.1.0
func WithFormat(format Format) LoadOption
WithFormat forces a specific format instead of auto-detection.
type Phase ¶
type Phase string
Phase indicates whether an event is the start or end of an operation.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin wraps a samber/do v2 container with audit logging hooks.
func New ¶
New creates an audit log plugin.
When Config.Enabled is false (the zero value), New checks the DO_AUDITLOG_ENABLED environment variable. Set it to "true", "1", or "yes" to enable audit logging without changing code. Explicitly setting Enabled to true overrides the env var.
If ContainerID is empty it defaults to "default".
Returns an error if Config.Validate() fails (e.g., ContainerID contains path separators).
Example ¶
plugin, injector := newPluginAndInjectorWithID("my-app")
do.ProvideValue(injector, "hello")
_ = do.MustInvoke[string](injector)
report := plugin.Report()
fmt.Println("services:", report.ServiceCount)
Output: services: 1
func (*Plugin) DroppedEventCount ¶ added in v0.0.3
DroppedEventCount returns the number of events dropped due to Config.MaxEvents.
func (*Plugin) EventsCount ¶
EventsCount returns the number of captured events without copying the slice.
func (*Plugin) ExportEventsToNDJSON ¶
ExportEventsToNDJSON writes every captured event as a line-delimited JSON stream to path.
func (*Plugin) ExportFilteredToFile ¶
func (p *Plugin) ExportFilteredToFile(path string, opts ...ReportOption) error
ExportFilteredToFile writes a filtered Report as indented JSON to path.
func (*Plugin) ExportToCSV ¶ added in v0.1.0
ExportToCSV writes the Report services as comma-separated values to path.
func (*Plugin) ExportToD2 ¶ added in v0.2.0
ExportToD2 writes the dependency graph as a D2 diagram to path.
func (*Plugin) ExportToDOT ¶ added in v0.2.0
ExportToDOT writes the dependency graph as a Graphviz DOT digraph to path.
func (*Plugin) ExportToFile ¶
ExportToFile writes the full Report as indented JSON to path.
Example ¶
plugin := mustNew(auditlog.Config{Enabled: true})
injector := do.NewWithOpts(plugin.Opts())
do.ProvideValue(injector, 42.0)
_ = do.MustInvoke[float64](injector)
path := os.Args[0] + ".audit.json"
err := plugin.ExportToFile(path)
if err != nil {
fmt.Println("export error:", err)
return
}
fmt.Println("exported")
Output: exported
func (*Plugin) ExportToHTML ¶
ExportToHTML writes a self-contained HTML visualization to a file.
func (*Plugin) ExportToHTMLTree ¶ added in v0.3.0
ExportToHTMLTree writes the service dependency DAG as an HTML tree to path.
func (*Plugin) ExportToMermaid ¶ added in v0.2.0
ExportToMermaid writes the dependency graph as a Mermaid flowchart to path.
func (*Plugin) ExportToPlantUML ¶ added in v0.2.0
ExportToPlantUML writes the dependency graph as a PlantUML component diagram to path.
func (*Plugin) ExportToTSV ¶ added in v0.1.0
ExportToTSV writes the Report services as tab-separated values to path.
func (*Plugin) ExportToTable ¶ added in v0.3.0
ExportToTable writes the service summary table to path in the specified format.
func (*Plugin) ExportToTree ¶ added in v0.3.0
ExportToTree writes the service dependency DAG as an ASCII tree to path.
func (*Plugin) Opts ¶
func (p *Plugin) Opts() *do.InjectorOpts
Opts returns a *do.InjectorOpts ready to pass to do.NewWithOpts. When Enabled is false the returned opts are harmless no-ops.
func (*Plugin) RecordHealthCheck ¶
RecordHealthCheck performs health checks on all services in the injector and records the results as audit events. It wraps injector.HealthCheck() with audit logging for each service result.
When the plugin is disabled, it delegates directly to the injector without recording.
Returns the same map[string]error as the underlying call (nil error = healthy).
Example ¶
plugin := mustNew(auditlog.Config{Enabled: true})
injector := do.NewWithOpts(plugin.Opts())
type dbConn struct{ Connected bool }
do.ProvideNamed(injector, "health-db", func(_ do.Injector) (*dbConn, error) {
return &dbConn{Connected: true}, nil
})
_ = do.MustInvokeNamed[*dbConn](injector, "health-db")
results := plugin.RecordHealthCheck(injector)
fmt.Println("services checked:", len(results))
Output: services checked: 1
func (*Plugin) RecordHealthCheckWithContext ¶
func (p *Plugin) RecordHealthCheckWithContext(ctx context.Context, injector do.Injector) map[string]error
RecordHealthCheckWithContext performs health checks on all services in the injector and records the results as audit events. It wraps injector.HealthCheckWithContext(ctx) with audit logging for each service result.
When the plugin is disabled, it delegates directly to the injector without recording.
Returns the same map[string]error as the underlying call (nil error = healthy).
func (*Plugin) Report ¶
Report returns a consolidated snapshot of everything observed so far.
Example ¶
plugin := mustNew(auditlog.Config{Enabled: true})
injector := do.NewWithOpts(plugin.Opts())
type reportConfig struct{ Val string }
do.ProvideNamed(injector, "report-cfg", func(_ do.Injector) (*reportConfig, error) {
return &reportConfig{Val: "prod"}, nil
})
_ = do.MustInvokeNamed[*reportConfig](injector, "report-cfg")
report := plugin.Report()
svc := report.ServiceByName("report-cfg")
if svc != nil {
fmt.Println("found service")
}
Output: found service
func (*Plugin) ReportFiltered ¶
func (p *Plugin) ReportFiltered(opts ...ReportOption) Report
ReportFiltered returns a filtered Report with the given options applied.
func (*Plugin) WriteD2 ¶ added in v0.2.0
WriteD2 writes the dependency graph as a D2 diagram to writer.
func (*Plugin) WriteDOT ¶ added in v0.2.0
WriteDOT writes the dependency graph as a Graphviz DOT digraph to writer.
func (*Plugin) WriteEventsNDJSON ¶
WriteEventsNDJSON writes every captured event as a line-delimited JSON stream to writer.
func (*Plugin) WriteHTMLTree ¶ added in v0.3.0
WriteHTMLTree writes the service dependency DAG as an HTML nested list tree to writer.
func (*Plugin) WriteMermaid ¶ added in v0.2.0
WriteMermaid writes the dependency graph as a Mermaid flowchart to writer.
func (*Plugin) WritePlantUML ¶ added in v0.2.0
WritePlantUML writes the dependency graph as a PlantUML component diagram to writer.
func (*Plugin) WriteReportCSV ¶ added in v0.1.0
WriteReportCSV writes the Report services as comma-separated values to writer.
func (*Plugin) WriteReportJSON ¶
WriteReportJSON writes the full Report as indented JSON to writer.
func (*Plugin) WriteReportTSV ¶ added in v0.1.0
WriteReportTSV writes the Report services as tab-separated values to writer.
func (*Plugin) WriteTable ¶ added in v0.3.0
func (p *Plugin) WriteTable(writer io.Writer, format output.Format, opts output.RenderOptions) error
WriteTable writes the service summary as a table in the specified format to writer.
type ProviderMeta ¶ added in v0.0.3
ProviderMeta holds display info for a ProviderType.
type ProviderType ¶
type ProviderType string
ProviderType describes how a service was registered in the DI container.
const ( ProviderTypeLazy ProviderType = "lazy" ProviderTypeEager ProviderType = "eager" ProviderTypeTransient ProviderType = "transient" ProviderTypeAlias ProviderType = "alias" )
func (ProviderType) Icon ¶
func (p ProviderType) Icon() string
Icon returns the samber/do canonical emoji for this provider type.
func (ProviderType) IsKnown ¶
func (p ProviderType) IsKnown() bool
IsKnown returns true if the provider type is a recognized value.
func (ProviderType) Label ¶ added in v0.1.0
func (p ProviderType) Label() string
Label returns the human-readable display label for this provider type.
func (ProviderType) String ¶
func (p ProviderType) String() string
String returns the provider type name.
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder captures DI lifecycle events in-memory with minimal overhead.
Locking Protocol ¶
All mutable state is protected by a single sync.RWMutex (mu):
Write path: mu.Lock() — all hook methods (OnBefore*, OnAfter*, RecordHealthCheck) Read path: mu.RLock() — BuildReport, Events, EventsCount, ResolveServiceScope
The invocation counter (invocationSeq) uses atomic.Int64, eliminating a separate mutex. Sequence numbers use a separate per-recorder atomic.Int64, also lock-free.
The onEvent callback is always called outside the lock to prevent user code from blocking or deadlocking the recorder.
Critical: enrichCapabilities and do.ExplainInjector ¶
BuildReport copies the scopes map under mu.RLock, then releases the lock BEFORE calling enrichCapabilities. This is mandatory because do.ExplainInjector acquires internal samber/do locks that would deadlock if called from inside any hook (which holds mu).
func NewRecorder ¶
NewRecorder creates a new event recorder.
func (*Recorder) BuildReport ¶
BuildReport assembles a machine-readable Report from all captured events.
func (*Recorder) DroppedEventCount ¶ added in v0.0.3
DroppedEventCount returns the number of events dropped due to MaxEvents cap.
func (*Recorder) EventsCount ¶
EventsCount returns the number of captured events without copying the slice.
func (*Recorder) OnAfterInvocation ¶
func (*Recorder) OnAfterRegistration ¶
func (*Recorder) OnAfterShutdown ¶
func (*Recorder) OnBeforeInvocation ¶
func (*Recorder) OnBeforeRegistration ¶
func (*Recorder) OnBeforeShutdown ¶
func (*Recorder) RecordHealthCheck ¶
RecordHealthCheck records a single health check result for a service.
type Report ¶
type Report struct {
Version string `json:"version"`
ContainerID string `json:"container_id"`
ExportedAt time.Time `json:"exported_at"`
EventCount int `json:"event_count"`
ServiceCount int `json:"service_count"`
ScopeCount int `json:"scope_count"`
TotalBuildDurationMs float64 `json:"total_build_duration_ms"`
TotalShutdownDurationMs float64 `json:"total_shutdown_duration_ms"`
ShutdownSucceeded bool `json:"shutdown_succeeded"`
// HealthCheckSucceeded is true when at least one service was health-checked
// and all passed. It is false when no health checks ran (HealthCheckedCount == 0)
// or when any service failed its check.
HealthCheckSucceeded bool `json:"health_check_succeeded"`
HealthCheckedCount int `json:"health_checked_count"`
// DroppedEventCount is the number of events dropped due to Config.MaxEvents.
// Always 0 when MaxEvents is 0 (unlimited).
DroppedEventCount int64 `json:"dropped_event_count"`
// Reconstructed is true when the report was built by ReplayEvents from a
// flat event stream rather than from live container hooks. Capability
// flags (IsHealthchecker, IsShutdowner) are always false on reconstructed
// reports, and the scope tree may be flattened.
Reconstructed bool `json:"reconstructed,omitempty"`
Events []Event `json:"events,omitempty"`
Services []ServiceInfo `json:"services"`
ScopeTree ScopeNode `json:"scope_tree"`
}
Report is a consolidated, machine-readable snapshot of the audit log.
func MergeReports ¶ added in v0.1.0
MergeReports combines multiple reports (e.g. from different containers or scopes) into a single report. Events are concatenated (preserving sequence order per-report but offsetting later reports to avoid collisions). Services are merged: services with the same scopeID + serviceName from different reports are kept separately only if they differ; duplicates are deduplicated. The scope trees are merged by union of all scopes.
The resulting report uses SchemaVersion, the latest ExportedAt, and a combined containerID ("merged"). All aggregate fields are recomputed.
func MigrateReport ¶
MigrateReport takes a raw JSON byte slice representing a report exported by a previous schema version and returns a Report compatible with the current SchemaVersion. Unknown fields are preserved through round-tripping.
In addition to upgrading older schemas, MigrateReport always re-derives the denormalized count and aggregate fields (EventCount, ServiceCount, ScopeCount, durations, health flags, per-service Status) from the actual data. This means current-schema input is also repaired: stale or hand-edited reports that would fail Validate() are normalized so the returned Report is always valid. The implied contract is "repair/normalize -> current", not just "upgrade old -> current".
For v0.1.0 → v0.2.0 the migration adds:
- scope_count, total_build_duration_ms, total_shutdown_duration_ms, shutdown_succeeded
- health_check_succeeded, health_checked_count (always false/0 for old reports)
- service_type, status, is_healthchecker, is_shutdowner (zero values)
func NewReport ¶ added in v0.1.0
func NewReport( version, containerID string, exportedAt time.Time, events []Event, services []ServiceInfo, scopeTree ScopeNode, ) (Report, error)
NewReport constructs a validated Report from its core data: the immutable identity/metadata fields and the three data slices (events, services, scope tree). It is the public, validated counterpart to the internal buildReportFromCore path used by BuildReport, Filtered, MigrateReport and ReplayEvents.
Per-service Status is re-derived from the underlying error/timestamp fields (via ServiceInfo.DeriveStatus), so callers never need to compute Status by hand and cannot construct a report whose Status would drift. All denormalized aggregate fields (counts, durations, health flags) are derived from the data.
Returns an error if the inputs are structurally inconsistent in a way that cannot be repaired by re-derivation.
func ReplayEvents ¶ added in v0.1.0
ReplayEvents reconstructs a Report from a flat event stream.
This is the inverse of the hook-based recording path: instead of live *do.Scope callbacks mutating a Recorder, it processes already-captured events to rebuild the same serviceRecord/scope state, then assembles a Report via the same buildReportFromCore finalizer.
Limitations (documented, not fixable without additional data):
- IsHealthchecker and IsShutdowner are always false (they require a live do.ExplainInjector call on a *do.Scope reference).
- Scope tree hierarchy is flattened (events carry scope_id/scope_name but not parent_id). The first-seen scope becomes root; all others are its direct children.
- DurationMs values are taken from the events themselves, not recomputed from wall-clock time.
The returned Report has Reconstructed=true so consumers can detect these limitations.
func (Report) Diff ¶ added in v0.0.4
func (r Report) Diff(other Report) DiffResult
Diff compares this report with another and returns the structural and status differences. Useful for regression-testing DI graphs across deploys.
The comparison key is (scope_id, service_name). Timestamps and durations are intentionally ignored — only structural changes (added/removed services, dependency edges, status transitions, error appearances) are reported.
func (Report) EventsByRef ¶
EventsByRef returns all events for the given scope ID and service name.
func (Report) EventsByService ¶
EventsByService returns all events for the given service name.
func (Report) EventsByType ¶
EventsByType returns all events matching the given event type.
func (Report) FailedServices ¶
func (r Report) FailedServices() []ServiceInfo
FailedServices returns all services with invocation or shutdown errors.
func (Report) Filtered ¶
func (r Report) Filtered(opts ...ReportOption) Report
Filtered returns a new Report with the given filters applied. Services and events that don't match any filter are removed. Summary fields (counts, durations) are recomputed from the filtered data. The scope tree is pruned to only include scopes with matching services.
Example ¶
plugin := mustNew(auditlog.Config{Enabled: true})
injector := do.NewWithOpts(plugin.Opts())
provideString(injector, "alpha", "a")
provideString(injector, "beta", "b")
_ = do.MustInvokeNamed[string](injector, "alpha")
_ = do.MustInvokeNamed[string](injector, "beta")
filtered := plugin.ReportFiltered(
auditlog.WithServicesByName("alpha"),
)
fmt.Println("filtered services:", filtered.ServiceCount)
Output: filtered services: 1
func (Report) Index ¶ added in v0.0.2
func (r Report) Index() ReportIndex
Index builds a lookup index for O(1) report queries. Useful when performing multiple lookups on the same report.
func (Report) ServiceByName ¶
func (r Report) ServiceByName(name string) *ServiceInfo
ServiceByName returns the first ServiceInfo matching the given exact service name. Returns nil if no service matches. For scoped lookup, use ServiceByRef.
func (Report) ServiceByRef ¶
func (r Report) ServiceByRef(scopeID, serviceName string) *ServiceInfo
ServiceByRef returns the ServiceInfo matching the given scope ID and service name. Returns nil if no service matches.
func (Report) ServicesByScope ¶
func (r Report) ServicesByScope(scopeID string) []ServiceInfo
ServicesByScope returns all services in the given scope.
func (Report) UnhealthyServices ¶
func (r Report) UnhealthyServices() []ServiceInfo
UnhealthyServices returns all services with a health check error.
func (Report) Validate ¶ added in v0.0.3
Validate checks internal consistency of the report: denormalized count fields must match the actual slice/tree lengths. Returns nil if consistent, or an error describing the first discrepancy found.
func (Report) WriteCSV ¶ added in v0.1.0
WriteCSV writes all services as comma-separated values to the writer. The first row is a header. Pointer fields render as empty strings when nil. Dependencies and dependents are semicolon-separated "scope/name" refs.
func (Report) WriteD2 ¶ added in v0.2.0
WriteD2 writes a D2 diagram representing the dependency graph to writer. Each service is a node; edges point from dependent -> dependency. The diagram title is set to the container ID for self-documenting output, and the warm-amber palette is applied per-node via D2 style directives. Edges are deduplicated locally via dedupGraphEdges because the D2 renderer has no built-in DedupEdges. See also WriteMermaid, WritePlantUML, and WriteDOT for the other diagram formats.
Example ¶
plugin := setupNamedRendererInjector()
var buf bytes.Buffer
err := plugin.Report().WriteD2(&buf)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("has edge:", bytes.Contains(buf.Bytes(), []byte("->")))
Output: has edge: true
func (Report) WriteDOT ¶ added in v0.1.0
WriteDOT writes a Graphviz DOT digraph representing the dependency graph. Each service is a node; edges point from dependent -> dependency. The output is valid input for `dot -Tsvg` / `dot -Tpng`. Nodes carry the warm-amber palette via per-node fillcolor/color attributes.
func (Report) WriteHTML ¶ added in v0.1.0
WriteHTML renders a self-contained HTML visualization of the report to writer. This enables offline report rendering from a loaded Report (e.g. via LoadReport) without a live Plugin/container.
func (Report) WriteHTMLTree ¶ added in v0.3.0
WriteHTMLTree writes the service dependency DAG as an HTML nested list tree. Nodes are labeled with service name and provider-type icon.
func (Report) WriteHTMLTreeString ¶ added in v0.3.0
WriteHTMLTreeString returns the HTML tree as a string.
func (Report) WriteJSON ¶ added in v0.0.4
WriteJSON writes the full report as indented JSON to the writer.
func (Report) WriteMermaid ¶
WriteMermaid writes a Mermaid flowchart representing the dependency graph. Each service is a node; edges point from dependent -> dependency. The warm -amber palette is applied per-node via style directives.
Example ¶
plugin := setupNamedRendererInjector()
var buf bytes.Buffer
err := plugin.Report().WriteMermaid(&buf)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("has header:", bytes.Contains(buf.Bytes(), []byte("flowchart TD")))
Output: has header: true
func (Report) WriteNDJSON ¶ added in v0.0.4
WriteNDJSON writes every event as a line-delimited JSON object (NDJSON). Operates directly on the Report.Events slice without a defensive copy, unlike Plugin.WriteEventsNDJSON which copies first.
func (Report) WritePlantUML ¶ added in v0.0.2
WritePlantUML writes a PlantUML component diagram representing the dependency graph. Each service is a component; edges point from dependent -> dependency. The warm-amber palette is applied per-node via PlantUML color specs. Paste the output into any tool that renders PlantUML.
func (Report) WriteTSV ¶ added in v0.1.0
WriteTSV writes all services as tab-separated values to the writer. Identical to WriteCSV but with a tab delimiter for tools that prefer TSV.
func (Report) WriteTable ¶ added in v0.3.0
WriteTable writes the service summary as a table in the specified format. Supported formats (when respective sub-modules are imported): table, json, csv, tsv, markdown, xml, d2, yaml, html, tree, mermaid, dot, jsonl, asciidoc, toml, plantuml.
func (Report) WriteTableString ¶ added in v0.3.0
WriteTableString returns the service summary table as a string in the specified format. See WriteTable for supported formats.
func (Report) WriteTree ¶ added in v0.3.0
WriteTree writes the service dependency DAG as an ASCII tree. Nodes are labeled with service name and provider-type icon.
func (Report) WriteTreeString ¶ added in v0.3.0
WriteTreeString returns the ASCII tree as a string.
type ReportIndex ¶ added in v0.0.2
type ReportIndex struct {
ByName map[string]*ServiceInfo
ByRef map[string]*ServiceInfo
ByScope map[string][]ServiceInfo
EventsByName map[string][]Event
EventsByRef map[string][]Event
EventsByType map[EventType][]Event
}
ReportIndex provides O(1) lookups into a Report. Build it once with report.Index() and reuse it for multiple queries.
type ReportOption ¶
type ReportOption func(*reportFilter)
ReportOption is a functional option for filtering a Report.
func WithEventsByType ¶
func WithEventsByType(eventType EventType) ReportOption
WithEventsByType filters the report to only include events with the given event type.
func WithScope ¶
func WithScope(scopeID string) ReportOption
WithScope filters the report to only include services and events in the given scope.
func WithServicesByName ¶
func WithServicesByName(names ...string) ReportOption
WithServicesByName filters the report to only include services with the given names.
func WithServicesByType ¶
func WithServicesByType(providerType ProviderType) ReportOption
WithServicesByType filters the report to only include services with the given provider type.
func WithTimeRange ¶
func WithTimeRange(from, to time.Time) ReportOption
WithTimeRange filters the report to only include events within the given time range.
type ScopeNode ¶
type ScopeNode struct {
ID string `json:"id"`
Name string `json:"name"`
Services []string `json:"services,omitempty"`
Children []ScopeNode `json:"children,omitempty"`
}
ScopeNode represents the scope hierarchy for visualization.
type ServiceDiff ¶ added in v0.0.4
type ServiceDiff struct {
ServiceRef
StatusChanged bool `json:"status_changed"`
InvocationCountDelta int `json:"invocation_count_delta"`
HealthCheckCountDelta int `json:"health_check_count_delta"`
HasNewError bool `json:"has_new_error"`
}
ServiceDiff describes changes to a service that exists in both reports.
type ServiceInfo ¶
type ServiceInfo struct {
ServiceRef
Status ServiceStatus `json:"status"`
ServiceType ProviderType `json:"service_type"`
RegisteredAt time.Time `json:"registered_at"`
FirstInvokedAt *time.Time `json:"first_invoked_at,omitempty"`
InvocationCount int `json:"invocation_count"`
InvocationOrder int `json:"invocation_order"`
FirstBuildDurationMs *float64 `json:"first_build_duration_ms,omitempty"`
Dependencies []ServiceRef `json:"dependencies,omitempty"`
Dependents []ServiceRef `json:"dependents,omitempty"`
ShutdownAt *time.Time `json:"shutdown_at,omitempty"`
ShutdownDurationMs *float64 `json:"shutdown_duration_ms,omitempty"`
ShutdownError *string `json:"shutdown_error,omitempty"`
InvocationError *string `json:"invocation_error,omitempty"`
IsHealthchecker bool `json:"is_healthchecker"`
IsShutdowner bool `json:"is_shutdowner"`
LastHealthCheckAt *time.Time `json:"last_health_check_at,omitempty"`
HealthCheckError *string `json:"health_check_error,omitempty"`
HealthCheckCount int `json:"health_check_count"`
}
ServiceInfo aggregates all observed data for a single service.
func (*ServiceInfo) DeriveStatus ¶ added in v0.0.4
func (s *ServiceInfo) DeriveStatus() ServiceStatus
DeriveStatus computes the lifecycle status from the service's own error pointers and invocation/shutdown timestamps. This is the canonical derivation — the stored Status field should always be populated via this method so it can never drift from the underlying data.
func (*ServiceInfo) HasHealthError ¶
func (s *ServiceInfo) HasHealthError() bool
HasHealthError returns true if the service has a health check error.
func (*ServiceInfo) RederiveStatus ¶ added in v0.2.0
func (s *ServiceInfo) RederiveStatus()
RederiveStatus sets Status to the value of DeriveStatus() in place. Use this on *ServiceInfo to repair stale or hand-edited statuses so the report always passes Validate().
func (*ServiceInfo) Uptime ¶
func (s *ServiceInfo) Uptime() time.Duration
Uptime returns the duration since the service was registered.
type ServiceRef ¶
type ServiceRef struct {
ScopeID string `json:"scope_id"`
ScopeName string `json:"scope_name"`
ServiceName string `json:"service_name"`
}
ServiceRef identifies a service within a specific scope. Embedded in Event and ServiceInfo for JSON flattening.
func (ServiceRef) IsRoot ¶
func (r ServiceRef) IsRoot() bool
IsRoot returns true if the service belongs to the root scope.
func (ServiceRef) String ¶
func (r ServiceRef) String() string
String returns a human-readable "scope/name" format for the service reference. Root scope services return just the service name.
type ServiceStatus ¶
type ServiceStatus string
ServiceStatus describes the lifecycle state of a service.
const ( ServiceStatusRegistered ServiceStatus = "registered" ServiceStatusActive ServiceStatus = "active" ServiceStatusInvocationError ServiceStatus = "invocation_error" ServiceStatusShutdown ServiceStatus = "shutdown" ServiceStatusShutdownError ServiceStatus = "shutdown_error" )
func (ServiceStatus) Icon ¶ added in v0.1.0
func (s ServiceStatus) Icon() string
Icon returns the display emoji for this service status.
func (ServiceStatus) IsError ¶
func (s ServiceStatus) IsError() bool
IsError returns true if the service has an invocation or shutdown error.
func (ServiceStatus) IsKnown ¶ added in v0.1.0
func (s ServiceStatus) IsKnown() bool
IsKnown returns true if the service status is a recognized value.
type StatusMeta ¶ added in v0.0.3
type StatusMeta struct {
Icon string `json:"icon"`
}
StatusMeta holds display info for a ServiceStatus.
type TypeMetadata ¶ added in v0.0.3
type TypeMetadata struct {
Providers map[string]ProviderMeta `json:"providers"`
Statuses map[string]StatusMeta `json:"statuses"`
Events map[string]EventMeta `json:"events"`
}
TypeMetadata provides display metadata (icons, labels, colors) for all enum types used in the HTML visualization. It is injected into the template as JSON so that JavaScript reads from a single Go-authoritative source instead of maintaining parallel hardcoded constants.
func BuildTypeMetadata ¶ added in v0.0.3
func BuildTypeMetadata() TypeMetadata
BuildTypeMetadata constructs display metadata from the Go enum constants. This is the single source of truth — the HTML template's JavaScript reads from the injected JSON rather than maintaining parallel constant definitions.
Source Files
¶
- csv.go
- d2.go
- daghtml_adapter.go
- diagram.go
- diff.go
- doc.go
- dot.go
- event.go
- export.go
- filter.go
- healthcheck.go
- hooks.go
- html.go
- html_templ.go
- loader.go
- mermaid.go
- metadata.go
- migration.go
- ndjson.go
- plantuml.go
- plugin.go
- recorder.go
- replay.go
- report.go
- report_builder.go
- report_helpers.go
- schema.go
- service.go
- table.go
- tree.go
- types.go
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
auditlog
command
Package main implements the auditlog CLI: report conversion, inspection, diffing and validation built on the samber-do-auditlog library.
|
Package main implements the auditlog CLI: report conversion, inspection, diffing and validation built on the samber-do-auditlog library. |
|
genschema
command
Package main is the JSON Schema generator for the auditlog report format.
|
Package main is the JSON Schema generator for the auditlog report format. |
|
Package main demonstrates EVERY major samber/do v2 feature, all observed by the audit-log plugin.
|
Package main demonstrates EVERY major samber/do v2 feature, all observed by the audit-log plugin. |