Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(log): Provided module #3

Merged
merged 1 commit into from
Jan 10, 2024
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
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
Loading