From: Anderson A. Giacomolli Date: Mon, 9 Nov 2026 13:19:42 +0000 (-0300) Subject: Stamp daemon log lines only outside the journal X-Git-Url: http://git.giacomolli.uk/onechat/commitdiff_plain/75b9579355e1adcdbb1255a7e8e4582e0a431f3d?ds=inline Stamp daemon log lines only outside the journal The daemon makes the logger of each part in one place. Under the journal, which sets JOURNAL_STREAM and adds its own time, the lines have no time stamp. Otherwise every line has the standard time stamp, the onechat lines too, so that no line has two time stamps and none has none. The start line names each value: the socket, the model and its effort, the directory where the commands run, whether WhatsApp is linked, and the number of active schedules. Issue: e4acfcb --- diff --git a/daemon.go b/daemon.go index f7ed315..4c0f466 100644 --- a/daemon.go +++ b/daemon.go @@ -29,7 +29,8 @@ const ( // the clients on the Unix socket in the data directory until ctx ends. When // telegram.json exists, it also runs the Telegram bot. It also keeps a linked // WhatsApp device connected. It holds the lock of the token file, so it is the -// only process that refreshes the tokens and opens the WhatsApp session. +// only process that refreshes the tokens and opens the WhatsApp session. Its +// log lines go to out, through the loggers of daemonLogger. func daemon(ctx context.Context, out io.Writer) (err error) { dir, err := dataDir() if err != nil { @@ -64,7 +65,8 @@ func daemon(ctx context.Context, out io.Writer) (err error) { }() whatsappSetDeviceProps() - whatsapp, err := openWhatsApp(ctx, dir, time.Now, log.New(out, "whatsapp: ", log.LstdFlags), whatsappNewClient) + logger := daemonLogger(out, "onechat: ") + whatsapp, err := openWhatsApp(ctx, dir, time.Now, daemonLogger(out, "whatsapp: "), whatsappNewClient) if err != nil { return err } @@ -108,18 +110,17 @@ func daemon(ctx context.Context, out io.Writer) (err error) { return err } - _, err = fmt.Fprintf(out, "onechat: %s on %s, %s at %s effort, in %s\n", agent.config.Name, listener.Addr(), agent.config.Model, agent.config.Effort, agent.config.Workdir) - if err != nil { - return errors.Join(err, listener.Close()) + linked := "WhatsApp is not linked" + if whatsapp.isLinked() { + linked = "WhatsApp is linked" } + logger.Printf("%s is ready: socket %s, model %s at %s effort, commands run in %s, %s, active schedules: %d", agent.config.Name, listener.Addr(), agent.config.Model, agent.config.Effort, agent.config.Workdir, linked, schedules.Len()) + var telegram *TelegramBot if hasBot { - telegram = newTelegramBot(newTelegramAPI(telegramAPIURL, botSettings.Token), botSettings.Owner, filepath.Join(agent.dir, telegramOffsetFileName), log.New(out, "telegram: ", log.LstdFlags)) - _, err = fmt.Fprintln(out, "onechat: the Telegram bot waits for messages of its owner") - if err != nil { - return errors.Join(err, listener.Close()) - } + telegram = newTelegramBot(newTelegramAPI(telegramAPIURL, botSettings.Token), botSettings.Owner, filepath.Join(agent.dir, telegramOffsetFileName), daemonLogger(out, "telegram: ")) + logger.Print("the Telegram bot waits for messages of its owner") } return daemonServe(ctx, listener, telegram, whatsapp, schedules, func(notify func(Event)) (*TurnLoop, error) { @@ -421,6 +422,18 @@ func daemonRunRequest(l *TurnLoop, whatsapp *WhatsApp, request SocketRequest, an } } +// daemonLogger returns the logger of the part of the daemon with prefix, such +// as "telegram: ", on out. The journal adds the time to each line and sets +// JOURNAL_STREAM for a service, so then the lines have no time stamp. Otherwise +// each line has the standard time stamp. +func daemonLogger(out io.Writer, prefix string) *log.Logger { + if os.Getenv("JOURNAL_STREAM") != "" { + return log.New(out, prefix, 0) + } + + return log.New(out, prefix, log.LstdFlags) +} + // durationText returns d for a log line: rounded to the second, or to the // millisecond when it is shorter than a second. func durationText(d time.Duration) string { diff --git a/daemon_test.go b/daemon_test.go index 8bc8c6e..9f45792 100644 --- a/daemon_test.go +++ b/daemon_test.go @@ -98,6 +98,35 @@ func TestDaemonWritesNoFileForALongSocketPath(t *testing.T) { require.Empty(t, entries) } +func TestDaemonLogHasATimeStampOnlyOutsideTheJournal(t *testing.T) { + testCases := []struct { + name string + journal string + line string + }{ + { + name: "the journal adds the time itself", + journal: "8:12345", + line: `^onechat: the agent is ready\n$`, + }, + { + name: "a terminal gets the time in the line", + journal: "", + line: `^onechat: \d{4}/\d\d/\d\d \d\d:\d\d:\d\d the agent is ready\n$`, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Setenv("JOURNAL_STREAM", tc.journal) + + var out strings.Builder + daemonLogger(&out, "onechat: ").Print("the agent is ready") + require.Regexp(t, tc.line, out.String()) + }) + } +} + // daemonStartTest serves a new agent on a socket in a temporary directory and // returns the socket path. The commands of the agent run in a temporary // directory. diff --git a/schedule.go b/schedule.go index d27274b..f3dcfe2 100644 --- a/schedule.go +++ b/schedule.go @@ -209,6 +209,14 @@ func (s *Schedules) List() string { return strings.Join(lines, "\n") } +// Len returns the number of schedules. +func (s *Schedules) Len() int { + s.mutex.Lock() + defer s.mutex.Unlock() + + return len(s.file.Schedules) +} + // Remove removes the schedule id and returns the answer of the tool. func (s *Schedules) Remove(id int) (string, error) { s.mutex.Lock()