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.
Add to your go.mod:
go get github.com/alvianhanif/gocommonlogpackage 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)
}
}commonlog supports two send methods: WebClient (API-based) and Webhook (simple HTTP POST).
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 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 integration requires proper token configuration for authentication. You can configure Lark tokens in two ways:
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",
},
}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",
},
}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.
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
}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"
}
}- SendMethod:
MethodWebClient(token-based authentication) orMethodWebhook - 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:
trueto enable detailed debug logging of all internal processes
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:
LarkTokenConfigobject 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)
- INFO: Logs locally only
- WARN: Logs + sends alert
- ERROR: Always sends alert
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, "")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.
go testConfig: Configuration structAttachment: File attachment structProvider: Interface for alert providersLarkTokenConfig: Lark app credentialsChannelResolver: Interface for channel resolutionDefaultChannelResolver: Default channel resolver implementation
MethodWebClient: Send method (token-based authentication)MethodWebhook: Send method (simple HTTP POST)INFO,WARN,ERROR: Alert levels
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