Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,17 @@ tail -f ~/Library/Logs/ldcron/a1b2c3d4.log
tail -n 100 ~/Library/Logs/ldcron/a1b2c3d4.log
```

### ログローテーション

ログファイルはデフォルトでは無限に増え続けます。ldcronはnewsyslog(8)の設定を生成するコマンドを提供しており、すべてのldcronログファイルを自動的にローテーションできます。

```bash
# newsyslog設定を生成・インストール(初回のみ、sudo必要)
ldcron log setup-rotation | sudo tee /etc/newsyslog.d/com.ldcron.conf
```

生成される設定は、各ログファイルが1MBを超えた時点でローテーションし、gzip圧縮したアーカイブを3世代保持します。プロセスへのシグナル送信は不要です(launchdはジョブ実行ごとにログファイルを開き直すため)。newsyslogはシステムのlaunchdジョブにより毎時自動実行されるため、追加のスケジュール設定は不要です。

---

## ファイル配置
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,22 @@ tail -f ~/Library/Logs/ldcron/a1b2c3d4.log
tail -n 100 ~/Library/Logs/ldcron/a1b2c3d4.log
```

### Log rotation

Log files grow indefinitely by default. ldcron provides a command to generate
a newsyslog(8) configuration that automatically rotates all ldcron log files.

```bash
# Generate and install the newsyslog config (one-time setup, requires sudo)
ldcron log setup-rotation | sudo tee /etc/newsyslog.d/com.ldcron.conf
```

The generated configuration rotates each log file when it exceeds 1 MB,
keeps 3 compressed (gzip) archives, and requires no process signaling
(launchd reopens log files on each job execution). newsyslog runs
automatically every hour via a system launchd job, so no additional
scheduling is needed.

---

## File locations
Expand Down
56 changes: 56 additions & 0 deletions cmd/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package cmd

import (
"fmt"
"path/filepath"

"github.com/spf13/cobra"
)

var logCmd = &cobra.Command{
Use: "log",
Short: "Manage job log files",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
}

var logSetupRotationCmd = &cobra.Command{
Use: "setup-rotation",
Short: "Print newsyslog configuration for log rotation",
Long: `Print a newsyslog(8) configuration snippet that rotates ldcron log files.

The generated configuration uses a glob pattern to cover all ldcron log files
under ~/Library/Logs/ldcron/. newsyslog is a macOS system service that runs
every hour and rotates log files according to /etc/newsyslog.d/*.conf.

To install:
ldcron log setup-rotation | sudo tee /etc/newsyslog.d/com.ldcron.conf

The configuration rotates logs when they exceed 1 MB, keeps 3 compressed
archives, and requires no process signaling.`,
SilenceUsage: true,
RunE: runLogSetupRotation,
}

func init() {
logCmd.AddCommand(logSetupRotationCmd)
}

func runLogSetupRotation(cmd *cobra.Command, _ []string) error {
dir, err := logDirPath()
if err != nil {
return err
}
logPattern := filepath.Join(dir, "*.log")

w := cmd.OutOrStdout()
_, _ = fmt.Fprintf(w, "# ldcron log rotation — managed by ldcron\n")
_, _ = fmt.Fprintf(w, "# See newsyslog.conf(5) for format details.\n")
_, _ = fmt.Fprintf(w, "# logfilename\t\t\t\t\t\tmode\tcount\tsize\twhen\tflags\n")
// Flags: G=glob pattern, N=no signal to any process,
// B=don't insert a rotation-marker line into the new log file
_, _ = fmt.Fprintf(w, "%s\t644\t3\t1024\t*\tGNB\n", logPattern)
return nil
}
41 changes: 41 additions & 0 deletions cmd/log_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cmd

import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
)

func TestLogSetupRotation(t *testing.T) {
home, err := os.UserHomeDir()
if err != nil {
t.Fatal(err)
}
wantPattern := filepath.Join(home, "Library", "Logs", "ldcron", "*.log")

var buf bytes.Buffer
cmd := logSetupRotationCmd
cmd.SetOut(&buf)
t.Cleanup(func() { cmd.SetOut(nil) })

if err := cmd.RunE(cmd, nil); err != nil {
t.Fatalf("unexpected error: %v", err)
}

output := buf.String()

if !strings.Contains(output, wantPattern) {
t.Errorf("output should contain log pattern %q, got:\n%s", wantPattern, output)
}
if !strings.Contains(output, "GNB") {
t.Errorf("output should contain flags GNB, got:\n%s", output)
}
if !strings.Contains(output, "1024") {
t.Errorf("output should contain size 1024, got:\n%s", output)
}
if !strings.Contains(output, "644") {
t.Errorf("output should contain mode 644, got:\n%s", output)
}
}
11 changes: 9 additions & 2 deletions cmd/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@ func launchAgentsDir() (string, error) {
return filepath.Join(home, "Library", "LaunchAgents"), nil
}

func logDir() (string, error) {
func logDirPath() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("failed to get home directory: %w", err)
}
dir := filepath.Join(home, "Library", "Logs", "ldcron")
return filepath.Join(home, "Library", "Logs", "ldcron"), nil
}

func logDir() (string, error) {
dir, err := logDirPath()
if err != nil {
return "", err
}
if err := os.MkdirAll(dir, 0o755); err != nil {
return "", fmt.Errorf("failed to create log directory: %w", err)
}
Expand Down
6 changes: 6 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func init() {
rootCmd.AddCommand(listCmd)
rootCmd.AddCommand(removeCmd)
rootCmd.AddCommand(runCmd)
rootCmd.AddCommand(logCmd)

// Save the default subcommand help, then set a custom help for the root command.
defaultHelp := rootCmd.HelpFunc()
Expand Down Expand Up @@ -76,6 +77,7 @@ Commands:
list List all registered jobs
remove <id> Delete a job by ID
run <id> Run a job immediately
log setup-rotation Print newsyslog config for log rotation

Cron expression format (minute hour day month weekday):

Expand All @@ -84,6 +86,10 @@ Cron expression format (minute hour day month weekday):
"0 9 * * 1-5" Weekdays (Mon–Fri) at 9:00
"30 8 1 * *" 1st of every month at 8:30

Log rotation:

ldcron log setup-rotation | sudo tee /etc/newsyslog.d/com.ldcron.conf

Flags:

-h, --help Show this help message
Expand Down