GithubHelp home page GithubHelp logo

lecho's Introduction

lecho 🍅

Zerolog wrapper for Echo web framework.

Installation

For Echo v4:

go get github.com/ziflex/lecho/v3

For Echo v3:

go get github.com/ziflex/lecho

Quick start

package main 

import (
	"os"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"github.com/ziflex/lecho/v3"
)

func main() {
    e := echo.New()
    e.Logger = lecho.New(os.Stdout)
}

Using existing zerolog instance

package main 

import (
	"os"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"github.com/ziflex/lecho/v3"
        "github.com/rs/zerolog"
)

func main() {
    log := zerolog.New(os.Stdout)
    e := echo.New()
    e.Logger = lecho.From(log)
}

Options

import (
	"os",
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/ziflex/lecho/v3"
)

func main() {
    e := echo.New()
    e.Logger = lecho.New(
       os.Stdout,
       lecho.WithLevel(log.DEBUG),
       lecho.WithFields(map[string]interface{}{ "name": "lecho factory"}),
       lecho.WithTimestamp(),
       lecho.WithCaller(),
       lecho.WithPrefix("we ❤️ lecho"),
       lecho.WithHook(...),
       lecho.WithHookFunc(...),
    )
}

Middleware

Logging requests and attaching request id to a context logger

import (
	"os",
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/ziflex/lecho/v3"
	"github.com/rs/zerolog"
)

func main() {
    e := echo.New()
    logger := lecho.New(
            os.Stdout,
            lecho.WithLevel(log.DEBUG),
            lecho.WithTimestamp(),
            lecho.WithCaller(),
         )
    e.Logger = logger
    
    e.Use(middleware.RequestID())
    e.Use(lecho.Middleware(lecho.Config{
    	Logger: logger
    }))	
    e.GET("/", func(c echo.Context) error {
        c.Logger().Print("Echo interface")
        zerolog.Ctx(c.Request().Context()).Print("Zerolog interface")
	
	return c.String(http.StatusOK, "Hello, World!")
    })
}

Escalate log level for slow requests:

e.Use(lecho.Middleware(lecho.Config{
    Logger: logger,
    RequestLatencyLevel: zerolog.WarnLevel,
    RequestLatencyLimit: 500 * time.Millisecond,
}))

Nesting under a sub dictionary

e.Use(lecho.Middleware(lecho.Config{
        Logger: logger,
        NestKey: "request"
    }))
    // Output: {"level":"info","request":{"remote_ip":"5.6.7.8","method":"GET", ...}, ...}

Enricher

Enricher allows you to add additional fields to the log entry.

e.Use(lecho.Middleware(lecho.Config{
        Logger: logger,
        Enricher: func(c echo.Context, logger zerolog.Context) zerolog.Context {
            return e.Str("user_id", c.Get("user_id"))
        },
    }))
    // Output: {"level":"info","user_id":"123", ...}

Errors

Since lecho v3.4.0, the middleware does not automatically propagate errors up the chain. If you want to do that, you can set HandleError to true.

e.Use(lecho.Middleware(lecho.Config{
    Logger: logger,
    HandleError: true,
}))

Helpers

Level converters

import (
    "fmt",
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
    "github.com/labstack/gommon/log"
    "github.com/ziflex/lecho/v3"
)

func main() {
	var z zerolog.Level
	var e log.Lvl
	
    z, e = lecho.MatchEchoLevel(log.WARN)
    
    fmt.Println(z, e)
    
    e, z = lecho.MatchZeroLevel(zerolog.INFO)

    fmt.Println(z, e)
}

lecho's People

Contributors

dependabot[bot] avatar farens avatar jlsherrill avatar michalkurzeja avatar tdevelioglu avatar virb3 avatar ziflex avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

lecho's Issues

logging response body

@ziflex thanks for the reply. I was able to implement the MultiLevelWriter. I am writting to Stdout and file.
I have a new issue added the response from c.Response echo context to the lecho logger.

I've tried the following:

Echo Bodydump middleware as a separate log.
Enricher method:
Enricher: func(c echo.Context, logger zerolog.Context) zerolog.Context {
return e.Interface("response", c.Response())
},
Ideally I can update or modify the logger context with response for a single stdout/file log.
I think it would be cool to allow combining of Bodydump and Enricher somehow.
I've read through this Zerolog guild but didn't find any working solutions

Thanks

tests

what about the tests?

Integrating with existing zerologger undefined

Hi im using echo v4 and go1.19.3 and docs say to use v2 of lecho:

My code below

package main

import (
	"os"

	"github.com/labstack/echo/v4"
	"github.com/rs/zerolog"
	"github.com/ziflex/lecho/v3"
)

func main() {
	log := zerolog.New(os.Stdout)
	e := echo.New()
	e.Logger = lecho.From(log)

	//write log from context
	e.GET("/", func(c echo.Context) error {
		c.Logger().Print("Echo interface")
	})

}


// Output

lecho.New undefined (type *lecho.Logger has no field or method New)
lecho.New undefined (type *lecho.Logger has no field or method From)

Q: is there another version or am im missing something to integrate with zerolog?
I've tried:

import (
"github.com/ziflex/lecho/v3"
"github.com/ziflex/lecho"
)

Thanks in advance

Zerolog interface not print logs

In short, according to the title, when I use a zero log interface it can't print the logs, here the code snippet

	e.Logger().Print("Echo interface")
	zerolog.Ctx(e.Request().Context()).Print("Zerolog interface🔥🔥🔥🔥🔥")

and here is the log generated

{"time":"2022-03-22T14:22:32+07:00","message":"⇨ http server started on [::]:1213"}
{"id":"mv4c2Pbvxl8KxshWvz7qs5TP8iSRlCxS","level":"-","time":"2022-03-22T14:22:46+07:00","message":"Echo interface"}
{"level":"debug","id":"mv4c2Pbvxl8KxshWvz7qs5TP8iSRlCxS","remote_ip":"::1","host":"localhost:1213","method":"POST","uri":"/dialogflow/webhook","user_agent":"PostmanRuntime/7.29.0","status":200,"referer":"","latency":632.1662,"latency_human":"632.1662ms","bytes_in":"1827","bytes_out":"448","time":"2022-03-22T14:22:46+07:00"}

why did it happen? I want to print the log using a zero log interface

Support for providing a zerolog logger to use

Currently this always calls zerolog.New(out) to create a logger to use. It would be useful to be able to provide an instance to use instead, since this can then inherit configuration that is setup elsewhere - such as the output format.

Run Enricher after next to allow access to request specific context information

Currently it is not possible to use the Enricher to log context specific information that is added from other middlewares like for example the user ID.

Could calling the Enricher be moved to after the next() call to be able to log such information?

For example I have a middleware that loads sets the UserID in the context and wanted to add this information to the request logs.

Enricher: func(c echo.Context, logger zerolog.Context) zerolog.Context {
  userId := c.Get("UserID")
  if userId != nil {
    return logger.Str("user_id", userId.(string))
  }
  return logger.Str("user_id", "")
}

For an old version of Echo

May I suggest you tweak this code to adapt it to Echo Framework v4? It's probably worth keeping lecho and forking it to lechov4.

allow user-defined per-request log fields

I'm using opentelemetry (otel) to trace my echo requests, and I would like the otel traceID to be a field in the logs made by lecho. Currently lecho has special handling for X-Request-ID, it would be great if that were extensible.

My current workaround is a partial fork of lecho using echo's middleware.RequestLoggerWithConfig, and it'd be nice to drop that.

One possible API would look something like:

e := echo.New()
logger := lecho.New(os.Stdout)
e.Logger = logger
e.Use(lecho.Middleware(
	lecho.Config{Logger: logger},
	lecho.WithPerRequestFields(func (c echo.Context, evt *zerolog.Event){
		// TODO: pull traceID off `c`, add to `evt`
	}),
))	

or maybe lump it into the logger itself:

e := echo.New()
logger := lecho.New(os.Stdout, lecho.WithPerRequestFields(func (c echo.Context, evt *zerolog.Event){
	// TODO: pull traceID off `c`, add to `evt`
}))
e.Logger = logger
e.Use(lecho.Middleware(lecho.Config{Logger: logger}))	

Would you be open to this kind of addition?

Double running of echo.HTTPErrorHandler

I'm experiencing double calling of HTTPErrorHandler by lecho middleware logger.

First it's called if there was error during request processing, immediately when request is returned from middleware chain:

lecho/middleware.go

Lines 84 to 86 in 6b6fa8f

if err = next(c); err != nil {
c.Error(err)
}

Next request log is produced and sent, but the middleware returns the same error again up:

return err

This causes the error to be propagated up the chain and is again sent to HTTPErrorHandler function by echo itself:
https://github.com/labstack/echo/blob/4a1ccdfdc520eb90573a97a7d04fd9fc300c1629/echo.go#L619-L631

The "native" echo Logger middleware does not do this, the error is consumed by calling handler immediately (the same as lecho) and then error sent up the middleware chain is actual result (error) of writing the log.

Do I understand something wrongly (ie is this expected behavior and i'm supposed to somehow catch the error) or is this actual bug?

Existing zerolog instance is not reflected with full functionality

First of all, thanks for this middleware!

While providing an exsiting zerolog instance to lecho, it seems that the functionality is not fully used:
When using

output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}
logger := zerolog.New(output).With().Timestamp().Logger()

The following output is producted

2021-03-22T08:09:42+01:00 INF booting foo=true
2021-03-22T08:09:42+01:00 ??? {"message":"⇨ http server started on [::]:8282"}

If you apply a HTTP handler to the echo framework and make a HTTP call, it outputs:

2021-03-22T08:09:16+01:00 ??? {"level":"info","id":"mISgzePvkoXF02jSSNdiAn8Ns2IBjfvX","remote_ip":"3.135.189.179","host":"8dc74f618941.ngrok.io","method":"GET","uri":"/favicon.ico","user_agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36","status":404,"referer":"","error":"code=404, message=Not Found","latency":0.139483,"latency_human":"139.483µs","bytes_in":"0","bytes_out":"24"}

A few things are not reflected:

  • level is part of the message, rather then after the timestamp (see ???)
  • No default level is provided (see "http server started" message)
  • Request information is still in json format rather than as single logging attributes

Is it a mistake from my side or a (valid) limitation of this library?
Thank you @ziflex!

Code to reproduce

package main

import (
	"os"
	"time"

	"github.com/labstack/echo/v4"
	"github.com/rs/zerolog"
	"github.com/ziflex/lecho/v2"
)

func main() {
	output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}
	logger := zerolog.New(output).With().Timestamp().Logger()

	logger.Info().Bool("foo", true).Msg("booting")

	e := echo.New()
	e.HideBanner = true

	lechoLogger := lecho.New(logger)
	e.Logger = lechoLogger

	e.Use(lecho.Middleware(lecho.Config{
		Logger: lechoLogger,
	}))

	e.Start(":8282")
}

Check GlobalLevel for existing Zerolog instances

From the zerolog documentation:

// SetGlobalLevel sets the global override for log level. If this
// values is raised, all Loggers will use at least this value.
//
// To globally disable logs, set GlobalLevel to Disabled.

I've found a problem where the echo middleware didn't log requests using an existing zerolog instance because by default the level set in log is TraceLevel here https://github.com/rs/zerolog/blob/master/log.go#L213 which maps to zerolog.NoLevel or log.OFF in lecho. However, in zerolog we usually use the SetGlobalLevel to set the log level. One way would be to consider the zerolog.Level value when calculating the log level https://github.com/ziflex/lecho/blob/master/options.go#L19:

func newOptions(log zerolog.Logger, setters []Setter) *Options {
	elvl, _ := MatchZeroLevel(log.GetLevel())
...
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.