Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: CI

on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
test:
name: Test
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
go-version: ['1.23.0']

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Get dependencies
run: go mod download

- name: Run tests
run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./...

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.out
flags: unittests
name: codecov-umbrella

build:
name: Build
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
go-version: ['1.23.0']

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Build
run: go build -v ./...

release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
needs: [test, build]
if: github.event_name == 'push' && github.ref == 'refs/heads/master'

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'

- name: Install semantic-release
run: |
npm install -g semantic-release@24 \
conventional-changelog-conventionalcommits@8

- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
37 changes: 37 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"branches": ["master"],
"plugins": [
["@semantic-release/commit-analyzer", {
"preset": "conventionalcommits",
"releaseRules": [
{"type": "feat", "release": "minor"},
{"type": "fix", "release": "patch"},
{"type": "perf", "release": "patch"},
{"type": "revert", "release": "patch"},
{"type": "docs", "release": "patch"},
{"type": "chore", "release": false},
{"type": "refactor", "release": "patch"},
{"type": "test", "release": false},
{"type": "build", "release": false},
{"type": "ci", "release": false}
]
}],
["@semantic-release/release-notes-generator", {
"preset": "conventionalcommits",
"presetConfig": {
"types": [
{"type": "feat", "section": "Features"},
{"type": "fix", "section": "Bug Fixes"},
{"type": "perf", "section": "Performance Improvements"},
{"type": "revert", "section": "Reverts"},
{"type": "docs", "section": "Documentation"},
{"type": "refactor", "section": "Code Refactoring"},
{"type": "test", "section": "Tests", "hidden": true},
{"type": "build", "section": "Build System", "hidden": true},
{"type": "ci", "section": "CI", "hidden": true}
]
}
}],
["@semantic-release/github"]
]
}
217 changes: 216 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,216 @@
# casbin-opentelemetry-logger
# casbin-opentelemetry-logger

[![Go Report Card](https://goreportcard.com/badge/github.com/casbin/casbin-opentelemetry-logger)](https://goreportcard.com/report/github.com/casbin/casbin-opentelemetry-logger)
[![Go](https://github.com/casbin/casbin-opentelemetry-logger/actions/workflows/ci.yml/badge.svg)](https://github.com/casbin/casbin-opentelemetry-logger/actions/workflows/ci.yml)
[![Coverage Status](https://codecov.io/gh/casbin/casbin-opentelemetry-logger/branch/master/graph/badge.svg)](https://codecov.io/gh/casbin/casbin-opentelemetry-logger)
[![GoDoc](https://godoc.org/github.com/casbin/casbin-opentelemetry-logger?status.svg)](https://godoc.org/github.com/casbin/casbin-opentelemetry-logger)
[![Release](https://img.shields.io/github/release/casbin/casbin-opentelemetry-logger.svg)](https://github.com/casbin/casbin-opentelemetry-logger/releases/latest)
[![Discord](https://img.shields.io/discord/1022748306096537660?logo=discord&label=discord&color=5865F2)](https://discord.gg/S5UjpzGZjN)

An OpenTelemetry logger implementation for [Casbin](https://github.com/casbin/casbin), providing event-driven metrics collection for authorization events.

## Features

- **Event-Driven Logging**: Implements the Casbin Logger interface with support for event-driven logging
- **OpenTelemetry Metrics**: Exports comprehensive metrics using the OpenTelemetry standard
- **Customizable Event Types**: Filter which event types to log
- **Custom Callbacks**: Add custom processing for log entries
- **Context Support**: Support for custom contexts for propagation and cancellation

## Metrics Exported

### Enforce Metrics
- `casbin.enforce.total` - Total number of enforce requests (labeled by `allowed`, `domain`)
- `casbin.enforce.duration` - Duration of enforce requests in seconds (labeled by `allowed`, `domain`)

### Policy Operation Metrics
- `casbin.policy.operations.total` - Total number of policy operations (labeled by `operation`, `success`)
- `casbin.policy.operations.duration` - Duration of policy operations in seconds (labeled by `operation`)
- `casbin.policy.rules.count` - Number of policy rules affected by operations (labeled by `operation`)

## Installation

```bash
go get github.com/casbin/casbin-opentelemetry-logger
```

## Usage

### Basic Usage

```go
package main

import (
"context"

opentelemetrylogger "github.com/casbin/casbin-opentelemetry-logger"
"go.opentelemetry.io/otel"
)

func main() {
// Get a meter from your OpenTelemetry provider
meter := otel.Meter("casbin")

// Create logger
logger, err := opentelemetrylogger.NewOpenTelemetryLogger(meter)
if err != nil {
panic(err)
}

// Use with Casbin
// enforcer.SetLogger(logger)
}
```

### With Custom Context

```go
ctx := context.Background()
logger, err := opentelemetrylogger.NewOpenTelemetryLoggerWithContext(ctx, meter)
if err != nil {
panic(err)
}
```

### Configure Event Types

```go
// Only log specific event types
logger.SetEventTypes([]opentelemetrylogger.EventType{
opentelemetrylogger.EventEnforce,
opentelemetrylogger.EventAddPolicy,
})
```

### Add Custom Callback

```go
// Add custom processing for log entries
logger.SetLogCallback(func(entry *opentelemetrylogger.LogEntry) error {
fmt.Printf("Event: %s, Duration: %v\n", entry.EventType, entry.Duration)
return nil
})
```

## Event Types

The logger supports the following event types:

- `EventEnforce` - Authorization enforcement requests
- `EventAddPolicy` - Policy addition operations
- `EventRemovePolicy` - Policy removal operations
- `EventLoadPolicy` - Policy loading operations
- `EventSavePolicy` - Policy saving operations

## Complete Example with OTLP Exporter

```go
package main

import (
"context"
"log"
"time"

opentelemetrylogger "github.com/casbin/casbin-opentelemetry-logger"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)

func main() {
ctx := context.Background()

// Create OTLP exporter
exporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithEndpoint("localhost:4317"),
otlpmetricgrpc.WithInsecure(),
)
if err != nil {
log.Fatal(err)
}

// Create resource
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("casbin-app"),
),
)
if err != nil {
log.Fatal(err)
}

// Create meter provider
provider := metric.NewMeterProvider(
metric.WithReader(metric.NewPeriodicReader(exporter)),
metric.WithResource(res),
)
otel.SetMeterProvider(provider)

// Create logger
meter := otel.Meter("casbin")
logger, err := opentelemetrylogger.NewOpenTelemetryLogger(meter)
if err != nil {
log.Fatal(err)
}

// Use with Casbin enforcer
// enforcer.SetLogger(logger)

// Shutdown
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := provider.Shutdown(ctx); err != nil {
log.Printf("Error shutting down meter provider: %v", err)
}
}()
}
```

## OpenTelemetry Collector Configuration

To collect metrics from your application, configure the OpenTelemetry Collector:

```yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317

exporters:
prometheus:
endpoint: "0.0.0.0:8889"
logging:
loglevel: debug

service:
pipelines:
metrics:
receivers: [otlp]
exporters: [prometheus, logging]
```

## Visualization with Prometheus and Grafana

1. **Configure OpenTelemetry Collector** to export metrics to Prometheus (see above)
2. **Configure Prometheus** to scrape the OpenTelemetry Collector endpoint
3. **Import Grafana Dashboard** using similar panels as the Prometheus logger project

## License

This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Related Projects

- [Casbin](https://github.com/casbin/casbin) - An authorization library that supports access control models
- [OpenTelemetry](https://opentelemetry.io/) - Observability framework for cloud-native software
- [casbin-prometheus-logger](https://github.com/casbin/casbin-prometheus-logger) - Prometheus logger for Casbin
19 changes: 19 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module github.com/casbin/casbin-opentelemetry-logger

go 1.23.0

require (
go.opentelemetry.io/otel v1.33.0
go.opentelemetry.io/otel/metric v1.33.0
go.opentelemetry.io/otel/sdk/metric v1.33.0
)

require (
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/sdk v1.33.0 // indirect
go.opentelemetry.io/otel/trace v1.33.0 // indirect
golang.org/x/sys v0.29.0 // indirect
)
Loading
Loading