Comment écrire un journal dans un fichier


108

J'essaye d'écrire dans un fichier journal avec Go.

J'ai essayé plusieurs approches, qui ont toutes échoué. Voici ce que j'ai essayé:

func TestLogging(t *testing.T) {
    if !FileExists("logfile") {
        CreateFile("logfile")
    }
    f, err := os.Open("logfile")
    if err != nil {
        t.Fatalf("error: %v", err)
    }

    // attempt #1
    log.SetOutput(io.MultiWriter(os.Stderr, f))
    log.Println("hello, logfile")

    // attempt #2
    log.SetOutput(io.Writer(f))
    log.Println("hello, logfile")

    // attempt #3
    log.SetOutput(f)
    log.Println("hello, logfile")
}

func FileExists(name string) bool {
    if _, err := os.Stat(name); err != nil {
       if os.IsNotExist(err) {
            return false
        }
    }
    return true
}

func CreateFile(name string) error {
    fo, err := os.Create(name)
    if err != nil {
        return err
    }
    defer func() {
        fo.Close()
    }()
    return nil
}

Le fichier journal est créé, mais rien n'est jamais imprimé ou ajouté. Pourquoi?


2
Si vous déployez votre programme sous Linux, vous pouvez simplement écrire votre journal sur la sortie std puis diriger la sortie vers un fichier comme: ./program 2> & 1 | tee logs.txt . Il doit y avoir un autre moyen dans un autre système.
nvcnvn

Réponses:


165

os.Open() doit avoir fonctionné différemment dans le passé, mais cela fonctionne pour moi:

f, err := os.OpenFile("testlogfile", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
if err != nil {
    log.Fatalf("error opening file: %v", err)
}
defer f.Close()

log.SetOutput(f)
log.Println("This is a test log entry")

Basé sur la documentation Go, os.Open()ne fonctionne pas pour log.SetOutput, car il ouvre le fichier "pour lecture:"

func Open

func Open(name string) (file *File, err error) Openouvre le fichier nommé pour lecture. En cas de succès, les méthodes du fichier retourné peuvent être utilisées pour la lecture; le descripteur de fichier associé a mode O_RDONLY. S'il y a une erreur, elle sera de type *PathError.

ÉDITER

Déplacé defer f.Close()après if err != nilvérification


9
Ne pas différer Close avant de vérifier err pour nul!
Volker

Ce n'est pas une activité réellement nuisible à fermer dans tous les cas iirc. Ce n'est pas vrai pour tous les types, cependant.
Dustin

2
@Dustin fpourrait être nil, ce qui entraînerait une panique. Il errest donc conseillé de vérifier avant de reporter l'appel.
nemo

@AllisonSoucieux d'expliquer pourquoi Openne fonctionnera pas avec log.SetOutput?
nemo

1
Les autorisations les plus sûres sont 0644 ou même 0664 pour permettre la lecture / écriture de l'utilisateur, la lecture / écriture de l'utilisateur et du groupe, et dans les deux cas, interdire à tout le monde d'écrire.
Jonathan

39

Je préfère la simplicité et la flexibilité de la recommandation d'application à 12 facteurs pour la journalisation. Pour ajouter à un fichier journal, vous pouvez utiliser la redirection shell. L'enregistreur par défaut de Go écrit dans stderr (2).

./app 2>> logfile

Voir aussi: http://12factor.net/logs


ne sera pas une bonne pratique lorsque vous voulez démoniser les choses, surtout avec start-tsop-daemon
Shrey

3
@Shrey Systemd pourrait facilement s'occuper de la journalisation, ainsi que des fonctions de démarrage-arrêt.
WarGasm

Malgré cela, c'est une bonne pratique ou non, c'est le type d'exploitation forestière que je recherchais à Golang. Merci d'avoir partagé ça!
addicted

Y a-t-il quelque chose de similaire sous Windows?
surfmuggle

Was like $ cd /etc/systemd/system $ sudo vi app.service ExecStart=/bin/bash -c 'sudo go run main.go >> /home/ubuntu/go/src/html_menu_1/logfile' Me NOT workUbuntu 18.04.3
Ryosuke Hujisawa

21

J'imprime généralement les journaux à l'écran et j'écris également dans un fichier. J'espère que cela aide quelqu'un.

f, err := os.OpenFile("/tmp/orders.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
    log.Fatalf("error opening file: %v", err)
}
defer f.Close()
wrt := io.MultiWriter(os.Stdout, f)
log.SetOutput(wrt)
log.Println(" Orders API Called")

7

Cela fonctionne pour moi

  1. créé un package appelé logger.go

    package logger
    
    import (
      "flag"
      "os"
      "log"
      "go/build"
    )
    
    var (
      Log      *log.Logger
    )
    
    
    func init() {
        // set location of log file
        var logpath = build.Default.GOPATH + "/src/chat/logger/info.log"
    
       flag.Parse()
       var file, err1 = os.Create(logpath)
    
       if err1 != nil {
          panic(err1)
       }
          Log = log.New(file, "", log.LstdFlags|log.Lshortfile)
          Log.Println("LogFile : " + logpath)
    }
    1. importez le package où vous voulez vous connecter, par exemple main.go

      package main
      
      import (
         "logger"
      )
      
      const (
         VERSION = "0.13"
       )
      
      func main() {
      
          // time to use our logger, print version, processID and number of running process
          logger.Log.Printf("Server v%s pid=%d started with processes: %d", VERSION, os.Getpid(),runtime.GOMAXPROCS(runtime.NumCPU()))
      
      }

6

Si vous exécutez binary sur une machine Linux, vous pouvez utiliser un script shell.

écraser dans un fichier

./binaryapp > binaryapp.log

ajouter dans un fichier

./binaryapp >> binaryapp.log

écraser stderr dans un fichier

./binaryapp &> binaryapp.error.log

ajouter stderr dans un fichier

./binaryapp &>> binalyapp.error.log

il peut être plus dynamique en utilisant le fichier de script shell.


Bon à savoir, comment remplacer stderr pour se connecter.
impossible

5

L'enregistreur par défaut de Go écrit dans stderr (2). rediriger vers le fichier

import ( 
    "syscall"
    "os" 
 )
func main(){
  fErr, err = os.OpenFile("Errfile", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
  syscall.Dup2(int(fErr.Fd()), 1) /* -- stdout */
  syscall.Dup2(int(fErr.Fd()), 2) /* -- stderr */

}

5

Déclarez en haut dans votre global varafin que tous vos processus puissent y accéder si nécessaire.

package main

import (
    "log"
    "os"
)
var (
    outfile, _ = os.Create("path/to/my.log") // update path for your needs
    l      = log.New(outfile, "", 0)
)

func main() {
    l.Println("hello, log!!!")
}

Hey @CostaHuang, veuillez laisser des commentaires détaillés. Merci
openwonk

@CostaHuang, je viens d'exécuter mon extrait de code et cela fonctionne.
openwonk

Salut @openwonk, j'ai testé à nouveau et cela n'a pas fonctionné sur mon ordinateur. Ma version est go version go1.10.2 windows/amd64, quelle est la vôtre?
Costa Huang

@CostaHuang, je viens de lancer l'exemple avec la même configuration que vous. L'exemple suppose que vous avez déjà configuré une structure de dossiers. Il existe des moyens simples de vérifier cela, mais mon objectif avec l'exemple est de montrer à quel point l'écriture dans un fichier journal est relativement simple. Changez votre code en outfile, _ = os.Create("my.log")et cela fonctionnera comme prévu.
openwonk

Votre code fonctionne. J'utilisais outfile, _ = os.Create("./path/to/my.log"). D'une certaine manière, je m'attendais à ce que le code crée les path/todossiers et le my.logfichier, mais apparemment cela n'a pas fonctionné. Je vous suggère de modifier votre réponse pour être outfile, _ = os.Create("./my.log"). De cette façon, nous savons clairement qu'il crée un journal dans le dossier actuel.
Costa Huang

5

En me basant sur la réponse d'Allison et Deepak, j'ai commencé à utiliser logrus et j'aime vraiment ça:

var log = logrus.New()

func init() {

    // log to console and file
    f, err := os.OpenFile("crawler.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        log.Fatalf("error opening file: %v", err)
    }
    wrt := io.MultiWriter(os.Stdout, f)

    log.SetOutput(wrt)
}

J'ai un différer f.Close () dans la fonction principale


0

J'écris des journaux dans les fichiers, qui sont générés quotidiennement (par jour, un fichier journal est généré). Cette approche fonctionne bien pour moi:

var (
    serverLogger *log.Logger
)

func init() {
    // set location of log file
    date := time.Now().Format("2006-01-02")
    var logpath = os.Getenv(constant.XDirectoryPath) + constant.LogFilePath + date + constant.LogFileExtension
    os.MkdirAll(os.Getenv(constant.XDirectoryPath)+constant.LogFilePath, os.ModePerm)
    flag.Parse()
    var file, err1 = os.OpenFile(logpath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)

    if err1 != nil {
        panic(err1)
    }
    mw := io.MultiWriter(os.Stdout, file)
    serverLogger = log.New(mw, constant.Empty, log.LstdFlags)
    serverLogger.Println("LogFile : " + logpath)
}

// LogServer logs to server's log file
func LogServer(logLevel enum.LogLevel, message string) {
    _, file, no, ok := runtime.Caller(1)
    logLineData := "logger_server.go"
    if ok {
        file = shortenFilePath(file)
        logLineData = fmt.Sprintf(file + constant.ColonWithSpace + strconv.Itoa(no) + constant.HyphenWithSpace)
    }
    serverLogger.Println(logLineData + logLevel.String() + constant.HyphenWithSpace + message)
}

// ShortenFilePath Shortens file path to a/b/c/d.go tp d.go
func shortenFilePath(file string) string {
    short := file
    for i := len(file) - 1; i > 0; i-- {
        if file[i] == constant.ForwardSlash {
            short = file[i+1:]
            break
        }
    }
    file = short
    return file
}

"shortenFilePath ()" méthode utilisée pour obtenir le nom du fichier à partir du chemin complet du fichier. et la méthode "LogServer ()" est utilisée pour créer une instruction de journal formatée (contient: nom de fichier, numéro de ligne, niveau de journal, instruction d'erreur, etc.)


0

Pour aider les autres, je crée une fonction de journal de base pour gérer la journalisation dans les deux cas, si vous voulez que la sortie soit stdout, puis activez le débogage, il est simple de faire un indicateur de commutation afin que vous puissiez choisir votre sortie.

func myLog(msg ...interface{}) {
    defer func() { r := recover(); if r != nil { fmt.Print("Error detected logging:", r) } }()
    if conf.DEBUG {
        fmt.Println(msg)
    } else {
        logfile, err := os.OpenFile(conf.LOGDIR+"/"+conf.AppName+".log", os.O_RDWR | os.O_CREATE | os.O_APPEND,0666)
        if !checkErr(err) {
            log.SetOutput(logfile)
            log.Println(msg)
        }
        defer logfile.Close()
    }
}




0

peut-être que cela vous aidera (si le fichier journal existe, utilisez-le, s'il n'existe pas, créez-le):

package main

import (
    "flag"
    "log"
    "os"
)
//Se declara la variable Log. Esta será usada para registrar los eventos.
var (
    Log *log.Logger = Loggerx()
)

func Loggerx() *log.Logger {
    LOG_FILE_LOCATION := os.Getenv("LOG_FILE_LOCATION")
        //En el caso que la variable de entorno exista, el sistema usa la configuración del docker.
    if LOG_FILE_LOCATION == "" {
        LOG_FILE_LOCATION = "../logs/" + APP_NAME + ".log"
    } else {
        LOG_FILE_LOCATION = LOG_FILE_LOCATION + APP_NAME + ".log"
    }
    flag.Parse()
        //Si el archivo existe se rehusa, es decir, no elimina el archivo log y crea uno nuevo.
    if _, err := os.Stat(LOG_FILE_LOCATION); os.IsNotExist(err) {
        file, err1 := os.Create(LOG_FILE_LOCATION)
        if err1 != nil {
            panic(err1)
        }
                //si no existe,se crea uno nuevo.
        return log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
    } else {
                //si existe se rehusa.
        file, err := os.OpenFile(LOG_FILE_LOCATION, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
        if err != nil {
            panic(err)
        }
        return log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
    }
}

Pour plus de détails: https://su9.co/9BAE74B

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.