tulir / whatsmeow

Go library for the WhatsApp web multidevice API
https://go.mau.fi/whatsmeow
Mozilla Public License 2.0
2.25k stars 418 forks source link

How can I make a custom logger #201

Closed hajsf closed 2 years ago

hajsf commented 2 years ago

I'm interested in having a custom logger, so I can read the log result from inside my code, so I can analyze it and take the required action, same as explained at #200

Any thought or support please.

tulir commented 2 years ago

Just implement the interface and set your implementation as the logger, basic Go. https://github.com/tulir/whatsmeow/blob/main/util/log/log.go#L16-L23

hajsf commented 2 years ago

Just implement the interface and set your implementation as the logger

Thanks, I got it done as:

package main

import (
    "fmt"
    "strings"
    "time"

    waLog "go.mau.fi/whatsmeow/util/log"
)

type customLogger struct {
    Mod   string
    Color bool
    Min   int
}

var levelToInt = map[string]int{
    "":      -1,
    "DEBUG": 0,
    "INFO":  1,
    "WARN":  2,
    "ERROR": 3,
}

func (c *customLogger) outputf(level, msg string, args ...interface{}) {
    if levelToInt[level] < c.Min {
        return
    }

    if strings.Compare(level, "ERROR") == 0 {
        passer.data <- sseData{
            event:   "notification",
            message: fmt.Sprintf("%s [%s %s] %s", time.Now().Format("15:04:05.000"), c.Mod, level, fmt.Sprintf(msg, args...)),
        }
    }
}

func (c *customLogger) Errorf(msg string, args ...interface{}) { c.outputf("ERROR", msg, args...) }
func (c *customLogger) Warnf(msg string, args ...interface{})  { c.outputf("WARN", msg, args...) }
func (c *customLogger) Infof(msg string, args ...interface{})  { c.outputf("INFO", msg, args...) }
func (c *customLogger) Debugf(msg string, args ...interface{}) { c.outputf("DEBUG", msg, args...) }

func (c *customLogger) Sub(mod string) waLog.Logger {
    return &customLogger{Mod: fmt.Sprintf("%s/%s", c.Mod, mod), Color: c.Color, Min: c.Min}
}

func LogText(module string, minLevel string, color bool) waLog.Logger {
    return &customLogger{Mod: module, Color: color, Min: levelToInt[strings.ToUpper(minLevel)]}
}

Where passer is defined as:

type sseData struct {
    event, message string
}
type DataPasser struct {
    data       chan sseData
    logs       chan string
    connection chan struct{} // To control maximum allowed clients connections
}

const maxClients = 1

func init() {
    passer = &DataPasser{
        data:       make(chan sseData),
        logs:       make(chan string),
        connection: make(chan struct{}, maxClients),
    }
}

Now I can analyze the ERROR (or log) message at func (c *customLogger) outputf(level, msg string, args ...interface{}) { ... } or send it to the browser as Server Sent Event as:

package main

import (
    "fmt"
    "net/http"
)

type sseData struct {
    event, message string
}
type DataPasser struct {
    data       chan sseData
    logs       chan string
    connection chan struct{} // To control maximum allowed clients connections
}

func (p *DataPasser) HandleSignal(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")
    setupCORS(&w, r)

    flusher, ok := w.(http.Flusher)
    if !ok {
        http.Error(w, "Internal error", 500)
        return
    }

    fmt.Println("Client connected from IP:", r.RemoteAddr)

    if len(p.connection) > 0 {
        fmt.Fprint(w, "event: notification\ndata: Connection is opened in another browser/tap ...\n\n")
        flusher.Flush()
    }
    p.connection <- struct{}{}

    fmt.Fprint(w, "event: notification\ndata: Connecting to WhatsApp server ...\n\n")
    flusher.Flush()

    // Connect to the WhatsApp client
    go Connect()

    for {
        select {
        case data := <-p.data:
            switch {
            case len(data.event) > 0:
                fmt.Fprintf(w, "event: %v\ndata: %v\n\n", data.event, data.message)
            case len(data.event) == 0:
                fmt.Fprintf(w, "data: %v\n\n", data.message)
            }
            flusher.Flush()
        case <-r.Context().Done():
            <-p.connection
            fmt.Println("Connection closed from IP:", r.RemoteAddr)
            return
        }
    }
}

func setupCORS(w *http.ResponseWriter, req *http.Request) {
    (*w).Header().Set("Cache-Control", "no-cache")
    (*w).Header().Set("Access-Control-Allow-Origin", "*")
    (*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
    (*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
image