Skip to content

Commit

Permalink
feat(log): Provided module (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
ekkinox authored Jan 10, 2024
1 parent 06c796f commit dafbff9
Show file tree
Hide file tree
Showing 25 changed files with 1,798 additions and 11 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/common-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ on:
module:
required: false
type: string
default: '.'
default: "."
go_version:
required: false
type: string
default: '1.20.0'
default: "1.20.0"

permissions:
contents: read
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
go_version:
required: false
type: string
default: '1.20.0'
default: "1.20.0"

permissions:
contents: write
Expand All @@ -21,6 +21,7 @@ jobs:
matrix:
module:
- "config"
- "log"

runs-on: ubuntu-latest
steps:
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/log-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: "log-ci"

on:
push:
branches:
- "feat**"
- "fix**"
- "hotfix**"
- "chore**"
paths:
- "log/**.go"
- "log/go.mod"
- "log/go.sum"
pull_request:
types:
- opened
- synchronize
- reopened
branches:
- main
paths:
- "log/**.go"
- "log/go.mod"
- "log/go.sum"

jobs:
ci:
uses: ./.github/workflows/common-ci.yml
secrets: inherit
with:
module: "log"
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Yokai

[![Go version](https://img.shields.io/badge/go-%3E%3D1.20-blue)](https://go.dev/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

> Simple, modular, and observable Go framework.
<p align="center">
Expand All @@ -12,14 +15,14 @@ Yokai's documentation will be available soon.

## Modules

| Module | Description |
|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| [config](modules/config) | Config module based on [Viper](https://github.com/spf13/viper) |
| [generate](modules/generate) | Generation module based on [Google UUID](https://github.com/google/uuid) |
| [healthcheck](modules/healthcheck) | Health check module compatible with [K8s probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) |
| [httpclient](modules/httpclient) | Http client module based on [net/http](https://pkg.go.dev/net/http) |
| [log](modules/log) | Logging module based on [Zerolog](https://github.com/rs/zerolog) |
| [trace](modules/trace) | Tracing module based on [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-go) |
| Module | Description |
|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| [config](config) | Config module based on [Viper](https://github.com/spf13/viper) |
| [generate](generate) | Generation module based on [Google UUID](https://github.com/google/uuid) |
| [healthcheck](healthcheck) | Health check module compatible with [K8s probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) |
| [httpclient](httpclient) | Http client module based on [net/http](https://pkg.go.dev/net/http) |
| [log](log) | Logging module based on [Zerolog](https://github.com/rs/zerolog) |
| [trace](trace) | Tracing module based on [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-go) |

## Contributing

Expand Down
65 changes: 65 additions & 0 deletions log/.golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
run:
timeout: 5m
concurrency: 8

linters:
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- containedctx
- contextcheck
- cyclop
- decorder
- dogsled
- durationcheck
- errcheck
- errchkjson
- errname
- errorlint
- exhaustive
- forbidigo
- forcetypeassert
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
- gofmt
- goheader
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- gosimple
- govet
- grouper
- importas
- ineffassign
- interfacebloat
- logrlint
- maintidx
- makezero
- misspell
- nestif
- nilerr
- nilnil
- nlreturn
- nolintlint
- nosprintfhostport
- prealloc
- predeclared
- promlinter
- reassign
- staticcheck
- tenv
- thelper
- tparallel
- typecheck
- unconvert
- unparam
- unused
- usestdlibvars
- whitespace
163 changes: 163 additions & 0 deletions log/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Log Module

[![ci](https://github.com/ankorstore/yokai/actions/workflows/log-ci.yml/badge.svg)](https://github.com/ankorstore/yokai/actions/workflows/log-ci.yml)
[![go report](https://goreportcard.com/badge/github.com/ankorstore/yokai/log)](https://goreportcard.com/report/github.com/ankorstore/yokai/log)
[![codecov](https://codecov.io/gh/ankorstore/yokai/graph/badge.svg?token=5s0g5WyseS&flag=log)](https://app.codecov.io/gh/ankorstore/yokai/tree/main/log)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/ankorstore/yokai/log)](https://pkg.go.dev/github.com/ankorstore/yokai/log)

> Logging module based on [Zerolog](https://github.com/rs/zerolog).
<!-- TOC -->
* [Installation](#installation)
* [Documentation](#documentation)
* [Usage](#usage)
* [Context](#context)
* [Testing](#testing)
<!-- TOC -->

## Installation

```shell
go get github.com/ankorstore/yokai/log
```

## Documentation

This module provides a [Logger](logger.go), offering all [Zerolog](https://github.com/rs/zerolog) methods.

### Usage

To create a `Logger`:

```go
package main

import (
"os"

"github.com/ankorstore/yokai/log"
"github.com/rs/zerolog"
)

var logger, _ = log.NewDefaultLoggerFactory().Create()

// equivalent to:
var logger, _ = log.NewDefaultLoggerFactory().Create(
log.WithServiceName("default"), // adds {"service":"default"} to log records
log.WithLevel(zerolog.InfoLevel), // logs records with level >= info
log.WithOutputWriter(os.Stdout), // sends logs records to stdout
)
```

To use the `Logger`:

```go
package main

import (
"github.com/ankorstore/yokai/log"
)

func main() {
logger, _ := log.NewDefaultLoggerFactory().Create()

logger.Info().Msg("some message") // {"level:"info", "service":"default", "message":"some message"}
}
```

See [Zerolog](https://github.com/rs/zerolog) documentation for more details about available methods.

### Context

This module provides the `log.CtxLogger()` function that allow to extract the logger from a `context.Context`.

If no logger is found in context, a [default](https://github.com/rs/zerolog/blob/master/ctx.go) Zerolog based logger will be used.

### Testing

This module provides a [TestLogBuffer](logtest/buffer.go), recording log records to be able to assert on them after logging:

```go
package main

import (
"fmt"

"github.com/ankorstore/yokai/log"
"github.com/ankorstore/yokai/log/logtest"
)

func main() {
buffer := logtest.NewDefaultTestLogBuffer()

logger, _ := log.NewDefaultLoggerFactory().Create(log.WithOutputWriter(buffer))

logger.Info().Msg("some message example")

// test on attributes exact matching
hasRecord, _ := buffer.HasRecord(map[string]interface{}{
"level": "info",
"message": "some message example",
})

fmt.Printf("has record: %v", hasRecord) // has record: true

// test on attributes partial matching
containRecord, _ := buffer.ContainRecord(map[string]interface{}{
"level": "info",
"message": "message",
})

fmt.Printf("contain record: %v", containRecord) // contain record: true
}
```

You can also use the provided [test assertion helpers](logtest/assert.go) in your tests:
- `AssertHasLogRecord`: to assert on exact attributes match
- `AssertHasNotLogRecord`: to assert on exact attributes non match
- `AssertContainLogRecord`: to assert on partial attributes match
- `AssertContainNotLogRecord`: to assert on partial attributes non match

```go
package main_test

import (
"fmt"
"testing"

"github.com/ankorstore/yokai/log"
"github.com/ankorstore/yokai/log/logtest"
)

func TestLogger(t *testing.T) {
buffer := logtest.NewDefaultTestLogBuffer()

logger, _ := log.NewDefaultLoggerFactory().Create(log.WithOutputWriter(buffer))

logger.Info().Msg("some message example")

// assertion success
logtest.AssertHasLogRecord(t, buffer, map[string]interface{}{
"level": "info",
"message": "some message example",
})

// assertion success
logtest.AssertHasNotLogRecord(t, buffer, map[string]interface{}{
"level": "info",
"message": "some invalid example",
})

// assertion success
logtest.AssertContainLogRecord(t, buffer, map[string]interface{}{
"level": "info",
"message": "message",
})

// assertion success
logtest.AssertContainNotLogRecord(t, buffer, map[string]interface{}{
"level": "info",
"message": "invalid",
})
}
```
31 changes: 31 additions & 0 deletions log/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package log

import (
"context"

"github.com/rs/zerolog"
"go.opentelemetry.io/otel/trace"
)

// CtxLogger retrieves a [Logger] from a provided context (or creates and appends a new one if missing).
//
// It automatically adds the traceID and spanID log fields depending on current tracing context.
func CtxLogger(ctx context.Context) *Logger {
fields := make(map[string]interface{})

spanContext := trace.SpanContextFromContext(ctx)
if spanContext.HasTraceID() {
fields["traceID"] = spanContext.TraceID().String()
}
if spanContext.HasSpanID() {
fields["spanID"] = spanContext.SpanID().String()
}

if len(fields) > 0 {
logger := zerolog.Ctx(ctx).With().Fields(fields).Logger()

return &Logger{&logger}
}

return &Logger{zerolog.Ctx(ctx)}
}
Loading

0 comments on commit dafbff9

Please sign in to comment.