Giter Site home page Giter Site logo

samber / slog-echo Goto Github PK

View Code? Open in Web Editor NEW
77.0 3.0 11.0 112 KB

๐Ÿšจ Echo middleware for slog logger

Home Page: https://pkg.go.dev/github.com/samber/slog-echo

License: MIT License

Makefile 5.49% Go 94.51%
attribute echo echo-framework error errors go golang handler http log

slog-echo's Introduction

slog: Echo middleware

tag Go Version GoDoc Build Status Go report Coverage Contributors License

Echo middleware to log http requests using slog.

See also:

HTTP middlewares:

Loggers:

Log sinks:

๐Ÿš€ Install

# echo v4 (current)
go get github.com/samber/slog-echo

# echo v5 (alpha)
go get github.com/samber/slog-echo@echo-v5

Compatibility: go >= 1.21

No breaking changes will be made to exported APIs before v2.0.0.

๐Ÿ’ก Usage

Handler options

type Config struct {
	DefaultLevel     slog.Level
	ClientErrorLevel slog.Level
	ServerErrorLevel slog.Level

	WithUserAgent      bool
	WithRequestID      bool
	WithRequestBody    bool
	WithRequestHeader  bool
	WithResponseBody   bool
	WithResponseHeader bool
	WithSpanID         bool
	WithTraceID        bool

	Filters []Filter
}

Attributes will be injected in log payload.

Other global parameters:

slogecho.TraceIDKey = "trace-id"
slogecho.SpanIDKey = "span-id"
slogecho.RequestBodyMaxSize  = 64 * 1024 // 64KB
slogecho.ResponseBodyMaxSize = 64 * 1024 // 64KB
slogecho.HiddenRequestHeaders = map[string]struct{}{ ... }
slogecho.HiddenResponseHeaders = map[string]struct{}{ ... }

Minimal

import (
	"net/http"
	"os"
	"time"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	slogecho "github.com/samber/slog-echo"
	"log/slog"
)

// Create a slog logger, which:
//   - Logs to stdout.
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

// Echo instance
e := echo.New()

// Middleware
e.Use(slogecho.New(logger))
e.Use(middleware.Recover())

// Routes
e.GET("/", func(c echo.Context) error {
	return c.String(http.StatusOK, "Hello, World!")
})
e.GET("/error", func(c echo.Context) error {
	return echo.NewHTTPError(http.StatusInternalServerError, "I'm angry")
})

// Start server
e.Logger.Fatal(e.Start(":4242"))

// output:
// time=2023-10-15T20:32:58.926+02:00 level=INFO msg="Success" env=production request.time=2023-10-15T20:32:58.626+02:00 request.method=GET request.path=/ request.route="" request.ip=127.0.0.1:63932 request.length=0 response.time=2023-10-15T20:32:58.926+02:00 response.latency=100ms response.status=200 response.length=7 id=229c7fc8-64f5-4467-bc4a-940700503b0d

OTEL

logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

config := slogecho.Config{
	WithSpanID:  true,
	WithTraceID: true,
}

e := echo.New()
e.Use(slogecho.NewWithConfig(logger, config))
e.Use(middleware.Recover())

Custom log levels

logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

config := slogecho.Config{
	DefaultLevel:     slog.LevelInfo,
	ClientErrorLevel: slog.LevelWarn,
	ServerErrorLevel: slog.LevelError,
}

e := echo.New()
e.Use(slogecho.NewWithConfig(logger, config))
e.Use(middleware.Recover())

Verbose

logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

config := slogecho.Config{
	WithRequestBody: true,
	WithResponseBody: true,
	WithRequestHeader: true,
	WithResponseHeader: true,
}

e := echo.New()
e.Use(slogecho.NewWithConfig(logger, config))
e.Use(middleware.Recover())

Filters

logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

e := echo.New()
e.Use(
	slogecho.NewWithFilters(
		logger,
		slogecho.Accept(func (c echo.Context) bool {
			return xxx
		}),
		slogecho.IgnoreStatus(401, 404),
	),
)
e.Use(middleware.Recover())

Available filters:

  • Accept / Ignore
  • AcceptMethod / IgnoreMethod
  • AcceptStatus / IgnoreStatus
  • AcceptStatusGreaterThan / IgnoreStatusLessThan
  • AcceptStatusGreaterThanOrEqual / IgnoreStatusLessThanOrEqual
  • AcceptPath / IgnorePath
  • AcceptPathContains / IgnorePathContains
  • AcceptPathPrefix / IgnorePathPrefix
  • AcceptPathSuffix / IgnorePathSuffix
  • AcceptPathMatch / IgnorePathMatch
  • AcceptHost / IgnoreHost
  • AcceptHostContains / IgnoreHostContains
  • AcceptHostPrefix / IgnoreHostPrefix
  • AcceptHostSuffix / IgnoreHostSuffix
  • AcceptHostMatch / IgnoreHostMatch

Using custom time formatters

import (
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	slogecho "github.com/samber/slog-echo"
	slogformatter "github.com/samber/slog-formatter"
	"log/slog"
)

// Create a slog logger, which:
//   - Logs to stdout.
//   - RFC3339 with UTC time format.
logger := slog.New(
	slogformatter.NewFormatterHandler(
		slogformatter.TimezoneConverter(time.UTC),
		slogformatter.TimeFormatter(time.DateTime, nil),
	)(
		slog.NewTextHandler(os.Stdout, nil),
	),
)

// Echo instance
e := echo.New()

// Middleware
e.Use(slogecho.New(logger))
e.Use(middleware.Recover())

// Routes
e.GET("/", func(c echo.Context) error {
	return c.String(http.StatusOK, "Hello, World!")
})
e.GET("/error", func(c echo.Context) error {
	return echo.NewHTTPError(http.StatusInternalServerError, "I'm angry")
})

// Start server
e.Logger.Fatal(e.Start(":4242"))

// output:
// time=2023-10-15T20:32:58.926+02:00 level=INFO msg="Success" env=production request.time=2023-10-15T20:32:58Z request.method=GET request.path=/ request.route="" request.ip=127.0.0.1:63932 request.length=0 response.time=2023-10-15T20:32:58Z response.latency=100ms response.status=200 response.length=7 id=229c7fc8-64f5-4467-bc4a-940700503b0d

Using custom logger sub-group

logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

// Echo instance
e := echo.New()

// Middleware
e.Use(slogecho.New(logger.WithGroup("http")))
e.Use(middleware.Recover())

// Routes
e.GET("/", func(c echo.Context) error {
	return c.String(http.StatusOK, "Hello, World!")
})
e.GET("/error", func(c echo.Context) error {
	return echo.NewHTTPError(http.StatusInternalServerError, "I'm angry")
})

// Start server
e.Logger.Fatal(e.Start(":4242"))

// output:
// time=2023-10-15T20:32:58.926+02:00 level=INFO msg="Success" env=production http.request.time=2023-10-15T20:32:58.626+02:00 http.request.method=GET http.request.path=/ http.request.route="" http.request.ip=127.0.0.1:63932 http.request.length=0 http.response.time=2023-10-15T20:32:58.926+02:00 http.response.latency=100ms http.response.status=200 http.response.length=7 http.id=229c7fc8-64f5-4467-bc4a-940700503b0d

Add logger to a single route

logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

// Echo instance
e := echo.New()

// Middleware
e.Use(middleware.Recover())

// Routes
e.GET("/", func(c echo.Context) error {
	return c.String(http.StatusOK, "Hello, World!")
}, slogecho.New(logger))

// Start server
e.Logger.Fatal(e.Start(":4242"))

Adding custom attributes

logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

// Add an attribute to all log entries made through this logger.
logger = logger.With("env", "production")

// Echo instance
e := echo.New()

// Middleware
e.Use(slogecho.New(logger))
e.Use(middleware.Recover())

// Routes
e.GET("/", func(c echo.Context) error {
	// Add an attribute to a single log entry.
	slogecho.AddCustomAttributes(c, slog.String("foo", "bar"))
	return c.String(http.StatusOK, "Hello, World!")
})

// Start server
e.Logger.Fatal(e.Start(":4242"))

// output:
// time=2023-10-15T20:32:58.926+02:00 level=INFO msg="Success" env=production request.time=2023-10-15T20:32:58.626+02:00 request.method=GET request.path=/ request.route="" request.ip=127.0.0.1:63932 request.length=0 response.time=2023-10-15T20:32:58.926+02:00 response.latency=100ms response.status=200 response.length=7 id=229c7fc8-64f5-4467-bc4a-940700503b0d foo=bar

JSON output

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

// Echo instance
e := echo.New()

// Middleware
e.Use(slogecho.New(logger))
e.Use(middleware.Recover())

// Routes
e.GET("/", func(c echo.Context) error {
	return c.String(http.StatusOK, "Hello, World!")
})

// Start server
e.Logger.Fatal(e.Start(":4242"))

// output:
// {"time":"2023-10-15T20:32:58.926+02:00","level":"INFO","msg":"Success","env":"production","http":{"request":{"time":"2023-10-15T20:32:58.626+02:00","method":"GET","path":"/","route":"","ip":"127.0.0.1:55296","length":0},"response":{"time":"2023-10-15T20:32:58.926+02:00","latency":100000,"status":200,"length":7},"id":"04201917-d7ba-4b20-a3bb-2fffba5f2bd9"}}

๐Ÿค Contributing

Don't hesitate ;)

# Install some dev dependencies
make tools

# Run tests
make test
# or
make watch-test

๐Ÿ‘ค Contributors

Contributors

๐Ÿ’ซ Show your support

Give a โญ๏ธ if this project helped you!

GitHub Sponsors

๐Ÿ“ License

Copyright ยฉ 2023 Samuel Berthe.

This project is MIT licensed.

slog-echo's People

Contributors

abh avatar bakatz avatar dependabot[bot] avatar erendursun avatar kyasbal avatar mwasilew2 avatar samber avatar ttys3 avatar wobondar 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

Watchers

 avatar  avatar  avatar

slog-echo's Issues

Consider AddSource in HandlerOptions

AddSource provides callstack informations which is proposed by slog https://pkg.go.dev/golang.org/x/exp/slog#HandlerOptions.
However a different depth must be used because the slog-echo/middleware.go:285 is always referred to

7:24PM DBG slog-echo/middleware.go:285 Incoming request request.time="2024-03-06 19:24:10.452642353 +0100 CET" request.method=GET request.host=localhost:8080 request.path=/ request.query="" request.params=map[] request.route=/ request.ip=127.0.0.1 request.referer="" request.length=0 response.time="2024-03-06 19:24:10.452687598 +0100 CET" response.latency=45.245ยตs response.status=200 response.length=0
7:24PM DBG slog-echo/middleware.go:285 Incoming request request.time="2024-03-06 19:24:10.470362643 +0100 CET" request.method=GET request.host=localhost:8080 request.path=/assets/css/main.css request.query="" request.params=map[*:/css/main.css] request.route=/assets* request.ip=127.0.0.1 request.referer=http://localhost:8080/ request.length=0 response.time="2024-03-06 19:24:10.470477768 +0100 CET" response.latency=115.125ยตs response.status=304 response.length=0

Improve error handling

In a number of failure scenarios, this middleware was behaving in undesirable ways (e.g. panicking, logging wrong status code). See the related PR for more details.

Response length value always 0

response.length value is always 0

PR #26

{
  "time": "2024-04-12T18:53:18.421Z",
  "level": "INFO",
  "msg": "Incoming request",
  "request": {
    "time": "2024-04-12T18:53:18.421Z",
    "method": "POST",
    "host": "localhost:5001",
    "path": "/test",
    "query": "",
    "params": {},
    "route": "/test",
    "ip": "127.0.0.1",
    "referer": "",
    "length": 0
  },
  "response": {
    "time": "2024-04-12T18:53:18.421Z",
    "latency": 81333,
    "status": 200,
    "length": 0, <=== Invalid value
    "body": "{\"name\":\"John Doe\",\"email\":\"[email protected]\"}\n"
  }
}

Duplicate time in logs

When using the middleware, the time key is duplicated:

logger := slog.New(slog.NewJSONHandler(os.Stderr, nil))

e := echo.New()

config := slogecho.Config{
	WithRequestBody: true,
	WithRequestHeader: true,
	WithResponseHeader: true,
	WithSpanID:  true,
	WithTraceID: true,
}
e.Use(slogecho.NewWithConfig(logger, config))
{"time":"2023-11-20T21:08:54.385775304+01:00","level":"INFO","msg":"example","time":"2023-11-20T21:08:54.385747657+01:00","latency":180617,"method":"GET","path":"/example","route":"/example","status":404,"ip":"::1","user-agent":"curl/8.4.0","trace-id":"0a9a35e5aec25e80a4777bed077ea2f1","span-id":"975848c3ee022956","request":{"body":""},"request":{"header":{"User-Agent":["curl/8.4.0"]}},"request":{"header":{"Accept":["*/*"]}},"response":{"header":{"Content-Type":["application/json; charset=UTF-8"]}}}

path on a named route

When I have route with a named parameter:

e.GET("/users/:id", func(...) {})

The log output has:

{"path":"/users/:id"}

It does not show the route the user hit to match that route.

I believe this is in error.

Internal error is ignored

When return an echo.HTTPError, the Internal error should be logged but only the Message field sent to the client is actually getting logged

Support for Echo v5

Is it possible to support echo v5?

i.e.

With echo you can currently import "github.com/labstack/echo/v5" however this is incompatible with slog-echo which expects v4.

Found log is repeated `\u0000` in body with option WithRequestBody

My code

func main() {
	logger := slog.New(
		slogformatter.NewFormatterHandler(
			slogformatter.TimezoneConverter(time.UTC),
			slogformatter.TimeFormatter(time.RFC3339, nil),
		)(
			slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{}),
		),
	)

	// Echo instance
	e := echo.New()

	// Middleware
	config := slogecho.Config{WithRequestBody: true, WithResponseBody: true, WithRequestHeader: true, WithResponseHeader: true}
	e.Use(slogecho.NewWithConfig(logger, config))
	e.Use(middleware.Recover())
	e.POST("/bbb", func(c echo.Context) error {
		type AuthRequest struct {
			Username string `json:"username"`
			Password string `json:"password"`
		}
		var req AuthRequest
		if err := c.Bind(&req); err != nil {
			echo.NewHTTPError(http.StatusBadRequest, "A request error")
		}
		return c.JSON(http.StatusOK, "Hello, World!")
	})

	// Start server
	e.Logger.Fatal(e.Start(":4242"))
}

And send request with curl

curl --location 'http://localhost:4242/bbb' \
--header 'Content-Type: application/json' \
--data '{"username": "aw01","password": "Aa666666"}'

Result in terminal

{
    "time": "2024-01-04T15:10:16.671725+07:00",
    "level": "INFO",
    "msg": "Incoming request",
    "env": "production",
    "request": {
        "time": "2024-01-04T15:10:16.671283+07:00",
        "method": "POST",
        "host": "localhost:4242",
        "path": "/bbb",
        "query": "",
        "params": {},
        "route": "/bbb",
        "ip": "127.0.0.1",
        "referer": "",
        "length": 43,
        "body": "{\"username\": \"aw01\",\"password\": \"Aa666666\"}\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
        "header": {
            "Content-Length": [
                "43"
            ]
        },
        "header": {
            "User-Agent": [
                "curl/8.1.2"
            ]
        },
        "header": {
            "Accept": [
                "*/*"
            ]
        },
        "header": {
            "Content-Type": [
                "application/json"
            ]
        }
    },
    "response": {
        "time": "2024-01-04T15:10:16.671657+07:00",
        "latency": 374123,
        "status": 200,
        "length": 0,
        "body": "\"Hello, World!\"\n",
        "header": {
            "Content-Type": [
                "application/json; charset=UTF-8"
            ]
        }
    }
}

I found repeated \u0000 in field request.body. Should remove it ?

interface conversion: *slogecho.bodyWriter is not http.Hijacker: missing method Hijack

I'm trying to use the slog-echo handler in combination with the GraphQL library gqlgen. When a client tries to set up a GraphQL subscription, this touches upon some echo code that deals with upgrading the HTTP connection to a Websocket connection. There's a lot of stuff going on here, but ultimately, this ends up with echo trying to assert http.Hijacker and call the Hijack() method on a slogecho.bodyWriter instance. Unfortunately, this struct doesn't implement that particular function, leading to panic.

The assertion and subsequent call is located here:

https://github.com/labstack/echo/blob/60fc2fb1b76f5613fc41aa9315cad6e8c96c6859/response.go#L94

Duplicated request "header" attribute in "response"

The request header attribute group appears in the response group alongside the response header group, duplicating twice in the response group.

version: v1.12.2

with Config:

WithResponseHeader: true,
WithRequestHeader: true,

Produces JSON:

{
  "time": "2024-04-04T03:24:37.840554+03:00",
  "level": "INFO",
  "msg": "Incoming request",
  "request": {
    "time": "2024-04-04T03:24:37.840525+03:00",
    "method": "GET",
    "host": "localhost:5000",
    "path": "/health",
    "query": "",
    "params": {},
    "route": "/health",
    "ip": "127.0.0.1",
    "referer": "",
    "length": 0
  },
  "response": {
    "time": "2024-04-04T03:24:37.84054+03:00",
    "latency": 14417,
    "status": 200,
    "header": {           <<==== request headers
      "Connection": ["Keep-Alive"],
      "User-Agent": ["Apache-HttpClient/4.5.14 (Java/17.0.10)"],
      "Accept-Encoding": ["br,deflate,gzip,x-gzip"],
      "Accept": ["application/json"]
    },
    "length": 0,
    "header": {           <<==== response headers
      "Vary": ["Origin"],
      "Content-Type": ["application/json; charset=UTF-8"]
    }
  }
}

panic due to nil interface conversion

Release v1.2.0 introduced a potential panic due to nil interface conversion at https://github.com/samber/slog-echo/blob/afd2ac60077e6f6feeaed2da84825e14846bd683/middleware.go#L83C4-L83C61 if header X-Forwarded-For is not set.

how to reproduce

git clone https://github.com/samber/slog-echo.git
cd example
go mod tidy
go run example.go
# visit localhost:<port> in browser

log

echo: http: panic serving [::1]:38720: interface conversion: interface {} is nil, not string
goroutine 6 [running]:
net/http.(*conn).serve.func1()
        /usr/local/go/src/net/http/server.go:1868 +0xb9
panic({0x69c680?, 0xc0000f6120?})
        /usr/local/go/src/runtime/panic.go:920 +0x270
main.main.New.NewWithConfig.func8.1({0x763f78, 0xc000099040})
        /home/eren/tmp/slog-echo/middleware.go:83 +0xd5a
github.com/labstack/echo/v4.(*Echo).ServeHTTP(0xc0000d66c0, {0x760568?, 0xc0000fe000}, 0xc0000ec200)
        /home/eren/go/pkg/mod/github.com/labstack/echo/[email protected]/echo.go:669 +0x399
net/http.serverHandler.ServeHTTP({0xc000095f50?}, {0x760568?, 0xc0000fe000?}, 0x6?)
        /usr/local/go/src/net/http/server.go:2938 +0x8e
net/http.(*conn).serve(0xc0000d0510, {0x760df8, 0xc000095e60})
        /usr/local/go/src/net/http/server.go:2009 +0x5f4
created by net/http.(*Server).Serve in goroutine 1
        /usr/local/go/src/net/http/server.go:3086 +0x5cb

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.