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?

Allison A
la source
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

Allison A
la source
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

Philip Nelson
la source
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")
deepakssn
la source
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()))
      
      }
philip mudenyo
la source
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.

Adzimzf
la source
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 */

}
Sergey
la source
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!!!")
}
openwonk
la source
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

PeggyScott
la source
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.)

Hardik Bohra
la source
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()
    }
}



Cyberience
la source
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

José G. Mejía
la source