kataras / iris

The fastest HTTP/2 Go Web Framework. New, modern and easy to learn. Fast development with Code you control. Unbeatable cost-performance ratio :rocket:
https://www.iris-go.com
BSD 3-Clause "New" or "Revised" License
25.24k stars 2.47k forks source link

How to throw logs in JSON ? #1120

Closed c0ze closed 5 years ago

c0ze commented 6 years ago

I checked this issue for some pointers, but unfortunately not successful. How can I make iris putout logs in json ?

kataras commented 6 years ago

Hello, JSON format on terminal or JSON format on files? We have examples for both situtations.

kataras commented 6 years ago

For request logging, there is an option at request logger middleware that is called LogFunc if that is not nil then it is being called to write the log, if it is nil (which it is by-default) then the app.Logger().Info is being used to write the request logs.

So, if you want to change both application-specific messages and per-request logs you can simply read the golog's examples to see how you can add and change things to the app.Logger() instance. For example there is an example for customize the logs, a hook, called Handle, there you can create a custom json structure, add your information with the log line's information and print as JSON or save to a file of your desire.

If you need further help I will write an example just for you.

kataras commented 6 years ago
package main

import (
    "fmt"
    "os"
    "runtime"
    "strings"
    "time"

    "github.com/kataras/iris"
    "github.com/kataras/iris/middleware/logger"

    "github.com/kataras/golog"
)

const deleteFileOnExit = false

func main() {
    app := iris.New()

    logFile := newLogFile()
    defer func() {
        logFile.Close()
        if deleteFileOnExit {
            os.Remove(logFile.Name())
        }
    }()

    // Handle the logs by yourself using the `app.Logger#Handle` method.
    // Return true if that handled, otherwise will print to the screen.
    // You can also use the `app.Logger#SetOutput/AddOutput` to change or add
    // multi (io.Writer) outputs if you just want to print the message
    // somewhere else than the terminal screen.
    app.Logger().Handle(func(l *golog.Log) bool {
        _, fn, line, _ := runtime.Caller(5)

        var (
            // formatted date string based on the `golog#TimeFormat`, which can be customized.
            // Or use the golog.Log#Time field to get the exact time.Time instance.
            datetime = l.FormatTime()
            // the log's message level.
            level = golog.GetTextForLevel(l.Level, false)
            // the log's message.
            message = l.Message
            // the source code line of where it is called,
            // this can differ on your app, see runtime.Caller(%d).
            source = fmt.Sprintf("%s#%d", fn, line)
        )

        // You can always use a custom json structure and json.Marshal and logFile.Write(its result)
        // but it is faster to just build your JSON string by yourself as we do below.
        jsonStr := fmt.Sprintf(`{"datetime":"%s","level":"%s","message":"%s","source":"%s"}`, datetime, level, message, source)
        fmt.Fprintln(logFile, jsonStr)

        /* Example output:
        {"datetime":"2018/10/31 13:13","level":"[INFO]","message":"My server started","source":"c:/mygopath/src/github.com/kataras/iris/_examples/http_request/request-logger/request-logger-file-json/main.go#71"}
        */
        return true
    })

    r := newRequestLogger()

    app.Use(r)
    app.OnAnyErrorCode(r, func(ctx iris.Context) {
        ctx.HTML("<h1> Error: Please try <a href ='/'> this </a> instead.</h1>")
    })

    h := func(ctx iris.Context) {
        ctx.Writef("Hello from %s", ctx.Path())
    }

    app.Get("/", h)

    app.Get("/1", h)

    app.Get("/2", h)

    app.Logger().Info("My server started")
    // http://localhost:8080
    // http://localhost:8080/1
    // http://localhost:8080/2
    // http://lcoalhost:8080/notfoundhere
    app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
}

var excludeExtensions = [...]string{
    ".js",
    ".css",
    ".jpg",
    ".png",
    ".ico",
    ".svg",
}

func newRequestLogger() iris.Handler {
    c := logger.Config{
        Status: true,
        IP:     true,
        Method: true,
        Path:   true,
    }

    //  we don't want to use the logger
    // to log requests to assets and etc
    c.AddSkipper(func(ctx iris.Context) bool {
        path := ctx.Path()
        for _, ext := range excludeExtensions {
            if strings.HasSuffix(path, ext) {
                return true
            }
        }
        return false
    })

    return logger.New(c)
}

// get a filename based on the date, file logs works that way the most times
// but these are just a sugar.
func todayFilename() string {
    today := time.Now().Format("Jan 02 2006")
    return today + ".json"
}

func newLogFile() *os.File {
    filename := todayFilename()
    // open an output file, this will append to the today's file if server restarted.
    f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        panic(err)
    }

    return f
}

@c0ze ^

c0ze commented 5 years ago

thanks, this was helpful !

kataras commented 5 years ago

You are welcomed @c0ze !! Happy to serve!