Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

stable-local-log: Motoko Logging Library

A powerful, modular logging infrastructure for Internet Computer (IC) projects. Designed to be instantiated once and shared across canisters or classes, providing namespace-based filtering, log levels, buffered storage, and upgrade-safe state management.

Features

  • Shared, Central Logging: Single logger instance across modules or canisters.
  • Buffered Ring Buffer: Capped FIFO storage with O(1) replacement of the oldest entry at capacity.
  • Namespace & Level Filtering: Log by custom namespaces; filter by Debug, Info, Warn, Error, Fatal.
  • Flexible Querying: Pagination via take and prev, plus namespace and level filters.
  • Export & Clear: Retrieve or clear all or filtered logs for audit, analytics, or privacy.
  • Upgrade-Safe: Built with versioned migrations to preserve logs across canister upgrades.
  • Extensibility Hooks: onEvict callbacks for storing old data elsewhere.

Installation

Add to your mops.toml:

mops add stable-local-log

Import in your Motoko module:

import StableLocalLog "mo:stable-local-log";

Instantiation (ClassPlus Pattern)

Use the Init helper from ClassPlus to create your logger instance. You must supply:

  • org_icdevs_class_plus_manager: Your ClassPlusInitializationManager.
  • initialState: Local_log.initialState() for default state.
  • args: Optional InitArgs with min_level and bufferSize.
  • pullEnvironment: Function loader for Environment (required for cycle sharing).
  • onInitialize: Optional async setup hook.
  • onStorageChange: Hook invoked on state mutation.

Example:

let mkLogger = Local_log.Init({
  org_icdevs_class_plus_manager = myManager;
  initialState     = Local_log.initialState();
  args             = ?{
    min_level  = ?#Info;                   // filter out Debug
    bufferSize = ?2000;                    // ring buffer max entries
  };
  pullEnvironment = ?(() -> {
    var org_icdevs_timer_tool = ?myTimerTool;
    advanced = null;
    onEvict  = ?(func(entries) { Debug.print("Evicted entries: " # debug_show(entries.size())); });
  });
  onInitialize    = ?(func _(logger) async { /* warm-up or migration */ });
  onStorageChange = func(state) { /* persist state or metrics */ };
});
let logger = mkLogger();

mkLogger is a callable function; logger is the initialized Local_log instance.

API Reference

Types

  • LogLevel: #Debug, #Info, #Warn, #Error, #Fatal
  • LogEntry:
    { timestamp: Nat; message: Text; level: Nat; namespace: Text }
  • LogQuery:
    { namespaces: ?[Text]; level: ?LogLevel; take: ?Nat; prev: ?Nat }
  • LogExportResult:
    { exportedCount: Nat; exported: [LogEntry] }
  • InitArgs:
    { min_level: ?LogLevel; bufferSize: ?Nat }
  • Environment:
    { tt: TimerToolLib.TimerTool; advanced: ?{ icrc85: ICRC85Options }; onEvict: ?(LogEntry -> ()) }

Public Methods

Method Params Returns Description
log_add (message: Text, level: LogLevel, namespace: Text) () Add a log entry to one namespace
log_debug/info/warn/error/fatal (message: Text, namespace: Text) () Shortcut for log_add at respective level
log_query (q: LogQuery) [LogEntry] Retrieve filtered/paginated logs
log_export (q: LogQuery) LogExportResult Export logs matching query
log_clear () Nat Clear all entries and return count removed
log_size (namespaces: ?[Text], level: ?LogLevel) Nat Count entries matching filters
log_set_buffer_size (v: Nat) Nat Set max buffer size, truncate if needed
log_get_buffer_size () Nat Get current buffer size
log_set_min_level (level: LogLevel) Nat Set and return new minimum level
getState () State Inspect internal state (for debug/migration)

Usage Examples

// Add logs
logger.log_info("Service started", "app");
logger.log_error("Database error", "db");

// Query last 50 warnings or above
let recent = logger.log_query({ namespaces=null; level=?#Warn; take=?50; prev=null });

// Export and clear
let exportRes = logger.log_export({ namespaces=null; level=null; take=null; prev=null });
ignore logger.log_clear();

Advanced Topics

  • onEvict Callback: Handle entries dropped by ring buffer rollover.
  • Upgrade Migrations: Uses Migration.migrate to maintain state compatibility.
  • Cycle Sharing (ICRC85): Controlled via initialize_icrc85 and Environment.advanced settings.

Contributing

Contributions, issues, and pull requests are welcome! Please fork the repository and open a PR with your changes.

License

This project is open-sourced under the MIT License. See the LICENSE file for details.

OVS Default Behavior

This motoko class has a default OVS behavior that sends cycles to the developer to provide funding for maintenance and continued development. In accordance with the OVS specification and ICRC85, this behavior may be overridden by another OVS sharing heuristic or turned off. We encourage all users to implement some form of OVS sharing as it helps us provide quality software and support to the community.

Default behavior: 0.2 XDR per 10000 processed events processed per month up to 1 XDR;

Default Beneficiary: PanIndustrial.com

Dependent Libraries:

About

Upgrade-safe buffered logging component for Motoko canisters

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages