Skip to content

Repository files navigation

gocommonlog

CI Go Report Card License: MIT

A unified logging and alerting library for Go, supporting Slack and Lark integrations via WebClient and Webhook. Features configurable providers, alert levels, and file attachment support.

Installation

Add to your go.mod:

go get github.com/alvianhanif/gocommonlog

Usage

package main

import (
    commonlog "github.com/alvianhanif/gocommonlog"
)

func main() {
    cfg := commonlog.Config{
        SendMethod: commonlog.MethodWebClient,
        Channel:    "your_lark_channel_id",
        ProviderConfig: map[string]interface{}{
            "provider":   "lark", // or "slack"
            "token":      "app_id++app_secret", // for Lark, use "app_id++app_secret" format
            "slack_token": "xoxb-your-slack-token", // dedicated Slack token
            "lark_token": commonlog.LarkTokenConfig{ // dedicated Lark token
                AppID:     "your-app-id",
                AppSecret: "your-app-secret",
            },
            "redis_host": "localhost", // required for Lark
            "redis_port": "6379",      // required for Lark
        },
    }
    logger := commonlog.NewLogger(cfg)

    // Send error with attachment
    if err := logger.Send(commonlog.ERROR, "System error occurred", &commonlog.Attachment{URL: "https://example.com/log.txt"}, ""); err != nil {
        log.Printf("Failed to send alert: %v", err)
    }

    // Send info (logs only)
    logger.Send(commonlog.INFO, "Info message", nil, "")

    // Send to a specific channel
    if err := logger.SendToChannel(commonlog.ERROR, "Send to another channel", nil, "", "another-channel-id"); err != nil {
        log.Printf("Failed to send alert: %v", err)
    }

    // Send to a different provider dynamically
    if err := logger.CustomSend("slack", commonlog.ERROR, "Message via Slack", nil, "", "slack-channel"); err != nil {
        log.Printf("Failed to send alert: %v", err)
    }
}

Send Methods

commonlog supports two send methods: WebClient (API-based) and Webhook (simple HTTP POST).

WebClient Usage

WebClient uses the full API with authentication tokens:

cfg := commonlog.Config{
    SendMethod: commonlog.MethodWebClient,
    Channel:   "your_channel",
    ProviderConfig: map[string]interface{}{
        "provider":   "lark", // or "slack"
        "token":      "app_id++app_secret", // for Lark
        "slack_token": "xoxb-your-slack-token", // for Slack
        "lark_token": commonlog.LarkTokenConfig{
            AppID:     "your-app-id",
            AppSecret: "your-app-secret",
        },
        "redis_host": "localhost", // required for Lark
        "redis_port": "6379",      // required for Lark
    },
}

Webhook Usage

Webhook is simpler and requires only a webhook URL:

cfg := commonlog.Config{
    SendMethod: commonlog.MethodWebhook,
    Channel:    "optional-channel-override", // optional
    ProviderConfig: map[string]interface{}{
        "provider": "slack",
        "token":    "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
    },
}

Lark Token Configuration

Lark integration requires proper token configuration for authentication. You can configure Lark tokens in two ways:

Method 1: Combined Token Format

cfg := commonlog.Config{
    SendMethod: commonlog.MethodWebClient,
    Channel:    "your_channel_id",
    ProviderConfig: map[string]interface{}{
        "provider": "lark",
        "token":    "your_app_id++your_app_secret", // Combined format: app_id++app_secret
        "redis_host": "localhost", // Optional: enables caching
        "redis_port": "6379",
    },
}

Method 2: Dedicated Lark Token Object

cfg := commonlog.Config{
    SendMethod: commonlog.MethodWebClient,
    Channel:   "your_channel_id",
    ProviderConfig: map[string]interface{}{
        "provider": "lark",
        "lark_token": commonlog.LarkTokenConfig{
            AppID:     "your_app_id",
            AppSecret: "your_app_secret",
        },
        "redis_host": "localhost", // Optional: enables caching
        "redis_port": "6379",
    },
}

Lark Token Caching

When using Lark, the tenant_access_token is cached to reduce API calls and improve performance. The library supports both Redis and in-memory caching:

  • Redis Caching (recommended for production): Persistent across application restarts and shared between instances
  • In-Memory Caching (fallback): Automatic fallback when Redis is unavailable, with 90-minute token expiry

Token Expiry Details:

  • API tokens expire after 2 hours (7200 seconds)
  • Cached tokens expire after 90 minutes (5400 seconds) to ensure freshness
  • Chat ID mappings are cached for 30 days

Cache Keys:

  • Lark tokens: commonlog_lark_token:{app_id}:{app_secret}
  • Chat IDs: commonlog_lark_chat_id:{environment}:{channel_name}

See REDIS_SETUP.md for detailed Redis setup instructions including AWS ElastiCache configuration.

Channel Mapping

You can configure different channels for different alert levels using a channel resolver:

package main

import (
    commonlog "github.com/alvianhanif/gocommonlog"
)

func main() {
    // Create a channel resolver that maps alert levels to different channels
    resolver := &commonlog.DefaultChannelResolver{
        ChannelMap: map[int]string{
            commonlog.INFO:  "#general",
            commonlog.WARN:  "#warnings",
            commonlog.ERROR: "#alerts",
        },
        DefaultChannel: "#general",
    }

    // Create config with channel resolver
    config := commonlog.Config{
        SendMethod:      commonlog.MethodWebClient,
        ChannelResolver: resolver,
        ServiceName:     "user-service",
        Environment:     "production",
        ProviderConfig: map[string]interface{}{
            "provider": "slack",
            "token":    "xoxb-your-slack-bot-token",
        },
    }

    logger := commonlog.NewLogger(config)

    // These will go to different channels based on level
    logger.Send(commonlog.INFO, "Info message", nil, "")    // goes to #general
    logger.Send(commonlog.WARN, "Warning message", nil, "") // goes to #warnings
    logger.Send(commonlog.ERROR, "Error message", nil, "")  // goes to #alerts
}

Custom Channel Resolver

You can implement custom channel resolution logic:

type CustomResolver struct{}

func (r *CustomResolver) ResolveChannel(level int) string {
    switch level {
    case commonlog.ERROR:
        return "#critical-alerts"
    case commonlog.WARN:
        return "#monitoring"
    default:
        return "#general"
    }
}

Configuration Options

Common Settings

  • SendMethod: MethodWebClient (token-based authentication) or MethodWebhook
  • Channel: Target channel or chat ID (used if no resolver)
  • ChannelResolver: Optional resolver for dynamic channel mapping
  • ServiceName: Name of the service sending alerts
  • Environment: Environment (dev, staging, production)
  • Debug: true to enable detailed debug logging of all internal processes

ProviderConfig Settings

All provider-specific configuration is now done via the ProviderConfig map:

  • provider: "slack" or "lark"
  • token: API token for WebClient authentication or webhook URL for Webhook method
  • slack_token: Dedicated Slack token (optional, overrides token for Slack)
  • lark_token: LarkTokenConfig object with AppID and AppSecret (optional, overrides token for Lark)
  • redis_host: Redis host for Lark caching (optional)
  • redis_port: Redis port for Lark caching (optional)
  • redis_password: Redis password (optional)
  • redis_ssl: Enable SSL for Redis (optional)
  • redis_cluster_mode: Enable Redis cluster mode (optional)
  • redis_db: Redis database number (optional)
  • ProviderConfig: Map of provider-specific settings (e.g., Redis config for Lark)

Alert Levels

  • INFO: Logs locally only
  • WARN: Logs + sends alert
  • ERROR: Always sends alert

File Attachments

Provide a public URL. The library appends it to the message for simplicity.

attachment := &commonlog.Attachment{URL: "https://example.com/log.txt"}
logger.Send(commonlog.ERROR, "Error with log", attachment, "")

Trace Log Section

When IncludeTrace is set to true, you can pass trace information as the fourth parameter to Send():

trace := "goroutine 1 [running]:\nmain.main()\n    /app/main.go:15 +0x2f"
logger.Send(commonlog.ERROR, "System error occurred", nil, trace)

This will format the trace as a code block in the alert message.

Testing

go test

API Reference

Types

  • Config: Configuration struct
  • Attachment: File attachment struct
  • Provider: Interface for alert providers
  • LarkTokenConfig: Lark app credentials
  • ChannelResolver: Interface for channel resolution
  • DefaultChannelResolver: Default channel resolver implementation

Constants

  • MethodWebClient: Send method (token-based authentication)
  • MethodWebhook: Send method (simple HTTP POST)
  • INFO, WARN, ERROR: Alert levels

Functions

  • NewLogger(cfg Config) *Logger: Create a new logger
  • (*Logger) Send(level int, message string, attachment *Attachment, trace string) error: Send alert with optional attachment and trace
  • (*Logger) SendToChannel(level int, message string, attachment *Attachment, trace string, channel string) error: Send alert to specific channel
  • (*Logger) CustomSend(provider string, level int, message string, attachment *Attachment, trace string, channel string) error: Send alert with custom provider

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages