@@ -2,7 +2,6 @@ package main
22
33import (
44 "context"
5- "errors"
65 "fmt"
76 "io"
87 "io/ioutil"
@@ -15,6 +14,8 @@ import (
1514 "runtime"
1615 "strings"
1716
17+ "github.com/pkg/errors"
18+
1819 "github.com/mitchellh/go-homedir"
1920 "github.com/natefinch/lumberjack"
2021 "github.com/vartanbeno/go-reddit/reddit"
@@ -24,8 +25,6 @@ import (
2425 "gopkg.in/yaml.v2"
2526)
2627
27- var errNotAtTheDisco = errors .New ("Don't panic, just os.Exit(1)" )
28-
2928type subreddit struct {
3029 Name string
3130 Destination string
@@ -35,7 +34,7 @@ type subreddit struct {
3534
3635type config struct {
3736 Version string
38- LumberjackLogger * lumberjack.Logger
37+ LumberjackLogger * lumberjack.Logger `yaml:"lumberjacklogger"`
3938 Subreddits []subreddit
4039}
4140
@@ -50,28 +49,24 @@ func fileExists(fileName string) bool {
5049 return ! info .IsDir ()
5150}
5251
53- func downloadFile (URL string , fileName string , force bool ) error {
52+ func downloadFile (URL string , fileName string ) error {
5453 // https://golangbyexample.com/download-image-file-url-golang/
5554
56- if force == false && fileExists (fileName ) {
57- log .Printf ("File exists: %v\n " , fileName )
58- return nil
59- }
60-
6155 response , err := http .Get (URL )
6256 if err != nil {
57+ return errors .WithStack (err )
6358 }
6459 defer response .Body .Close ()
6560
6661 file , err := os .Create (fileName )
6762 if err != nil {
68- return err
63+ return errors . WithStack ( err )
6964 }
7065 defer file .Close ()
7166
7267 _ , err = io .Copy (file , response .Body )
7368 if err != nil {
74- return err
69+ return errors . WithStack ( err )
7570 }
7671 return nil
7772}
@@ -80,18 +75,14 @@ func genFilePath(destinationDir string, title string, fullURL string) (string, e
8075 // https://www.golangprograms.com/golang-download-image-from-given-url.html
8176 fileURL , err := url .Parse (fullURL )
8277 if err != nil {
83- return "" , err
78+ return "" , errors . WithStack ( err )
8479 }
8580
8681 path := fileURL .Path
8782 segments := strings .Split (path , "/" )
8883
8984 fileName := segments [len (segments )- 1 ]
9085
91- if err != nil {
92- return "" , err
93- }
94-
9586 for _ , s := range []string {" " , "/" , "\\ " , "\n " , "\r " , "\x00 " } {
9687 title = strings .ReplaceAll (title , s , "_" )
9788 }
@@ -104,13 +95,15 @@ func readConfig(configPath string) (*lumberjack.Logger, []subreddit, error) {
10495
10596 configBytes , err := ioutil .ReadFile (configPath )
10697 if err != nil {
107- return nil , nil , err
98+ // It's ok to not have a config (first run, for example)
99+ return nil , nil , errors .WithStack (err )
108100 }
109101
110102 cfg := config {}
111103 err = yaml .UnmarshalStrict (configBytes , & cfg )
112104 if err != nil {
113- return nil , nil , err
105+ // not ok to get invalid YAML
106+ log .Panicf ("readConfig: yaml decode: %+v\n " , errors .WithStack (err ))
114107 }
115108
116109 var lumberjackLogger * lumberjack.Logger = nil
@@ -119,21 +112,17 @@ func readConfig(configPath string) (*lumberjack.Logger, []subreddit, error) {
119112 if cfg .LumberjackLogger != nil {
120113 f , err := homedir .Expand (cfg .LumberjackLogger .Filename )
121114 if err != nil {
122- panic ( err )
115+ log . Panicf ( "readConfig: expand lumberjack: %+v \n " , errors . WithStack ( err ) )
123116 }
124117 cfg .LumberjackLogger .Filename = f
125118 lumberjackLogger = cfg .LumberjackLogger
126119 }
127120
128- // TODO: figure out memory management here
129- // why do I have to make a new list? Is it because I get a new
130- // copy of subreddit in the for loop and it just disappears each iteration?
131- // subreddits := make([]subreddit)
132121 subreddits := make ([]subreddit , 0 )
133122 for _ , sr := range cfg .Subreddits {
134123 fullDest , err := homedir .Expand (sr .Destination )
135124 if err != nil {
136- log .Panicf ("Cannot expand subreddit destination %v: %v: %v" , sr .Name , sr .Destination , err )
125+ log .Panicf ("readConfig: Cannot expand subreddit destination %v: %v: %v" , sr .Name , sr .Destination , err )
137126 }
138127 sr .Destination = fullDest
139128 subreddits = append (subreddits , sr )
@@ -142,7 +131,7 @@ func readConfig(configPath string) (*lumberjack.Logger, []subreddit, error) {
142131 return lumberjackLogger , subreddits , nil
143132}
144133
145- func grab (subreddits []subreddit ) error {
134+ func grab (sugar * zap. SugaredLogger , subreddits []subreddit ) error {
146135
147136 client , err := reddit .NewReadonlyClient ()
148137 if err != nil {
@@ -163,33 +152,69 @@ func grab(subreddits []subreddit) error {
163152 })
164153
165154 if err != nil {
166- log .Printf ("getTopPosts: %v: %v\n " , subreddit , err )
155+ // not fatal, we can continue with other subreddits
156+ sugar .Errorw ("Can't use subreddit" ,
157+ "subreddit" , subreddit ,
158+ "err" , errors .WithStack (err ),
159+ )
167160 }
168161
169162 for _ , post := range posts {
170163 if strings .HasSuffix (post .URL , ".jpg" ) {
171164
172165 filePath , err := genFilePath (subreddit .Destination , post .Title , post .URL )
173166 if err != nil {
174- log .Printf ("genFilePath: %v: %v: %v\n " , subreddit .Name , post .URL , err )
167+ sugar .Errorw ("genFilePath err" ,
168+ "subreddit" , subreddit .Name ,
169+ "url" , post .URL ,
170+ "err" , errors .WithStack (err ),
171+ )
172+ }
173+ if fileExists (filePath ) {
174+ sugar .Infow (
175+ "file exists" ,
176+ "filePath" , filePath ,
177+ )
178+ continue
175179 }
176- err = downloadFile (post .URL , filePath , false )
180+ err = downloadFile (post .URL , filePath )
177181 if err != nil {
178- log .Printf ("downloadFile: %v: %v: %v\n " , subreddit .Name , post .URL , err )
182+ sugar .Errorw (
183+ "downloadFile" ,
184+ "subreddit" , subreddit .Name ,
185+ "url" , post .URL ,
186+ "err" , errors .WithStack (err ),
187+ )
188+
179189 } else {
180- log .Printf ("downloaded file: %v: %v\n " , subreddit .Name , post .URL )
190+ sugar .Infow (
191+ "downloaded file" ,
192+ "subreddit" , subreddit .Name ,
193+ "filePath" , filePath ,
194+ "url" , post .URL ,
195+ )
181196 }
182197 } else {
183- log .Printf ("Could not download: %v: %v\n " , subreddit .Name , post .URL )
198+ sugar .Errorw (
199+ "Could not download" ,
200+ "subreddit" , subreddit .Name ,
201+ "url" , post .URL ,
202+ )
184203 }
185204 }
186205 }
187206
188207 return nil
189208}
190209
191- func editConfig (configPath string , editor string ) error {
192- emptyConfigContent := []byte (`version: 1.0.0
210+ func editConfig (sugar * zap.SugaredLogger , configPath string , editor string ) error {
211+ emptyConfigContent := []byte (`version: 2.0.0
212+ # make lumberjacklogger nil to not log to file
213+ lumberjacklogger:
214+ filename: ~/.config/grabbit.log
215+ maxsize: 5 # megabytes
216+ maxbackups: 0
217+ maxage: 30 # days
193218subreddits:
194219 - name: earthporn
195220 destination: ~/Pictures/grabbit
@@ -230,7 +255,11 @@ subreddits:
230255 return err
231256 }
232257
233- log .Printf ("Executing: %s %s" , executable , configPath )
258+ fmt .Printf ("Executing: %s %s\n " , executable , configPath )
259+ sugar .Infow ("Executing" ,
260+ "editor" , executable ,
261+ "configPath" , configPath ,
262+ )
234263
235264 cmd := exec .Command (executable , configPath )
236265 cmd .Stdin = os .Stdin
@@ -291,6 +320,7 @@ func newLogger(lumberjackLogger *lumberjack.Logger, fp *os.File, lvl zapcore.Lev
291320 combinedCore ,
292321 zap .AddCaller (),
293322 zap .AddStacktrace (stackTraceLvl ),
323+ // TODO: replace with version (goreleaser embeds it)
294324 zap .Fields (zap .Int ("pid" , os .Getpid ())),
295325 )
296326
@@ -299,16 +329,6 @@ func newLogger(lumberjackLogger *lumberjack.Logger, fp *os.File, lvl zapcore.Lev
299329
300330func run () error {
301331
302- // logger stuff
303- // lumberjack.Logger is already safe for concurrent use, so we don't need to
304- // lock it.
305- // lumberjackLogger := &lumberjack.Logger{
306- // Filename: "tmp.log",
307- // MaxSize: 5, // megabytes
308- // MaxBackups: 0,
309- // MaxAge: 30, // days
310- // }
311-
312332 // cli and go!
313333 app := kingpin .New ("grabbit" , "Get top images from subreddits" ).UsageTemplate (kingpin .DefaultUsageTemplate )
314334 app .HelpFlag .Short ('h' )
@@ -331,37 +351,40 @@ func run() error {
331351
332352 logger := newLogger (
333353 lumberjackLogger ,
334- os .Stderr ,
354+ nil , // TODO: get an os.File from readConfig to put in here - os. Stderr
335355 zap .DebugLevel ,
336356 zap .ErrorLevel ,
337357 )
338358
339359 defer logger .Sync ()
340360 sugar := logger .Sugar ()
341361
362+ logErrAndReturn := func (err error ) error {
363+ if err != nil {
364+ sugar .Errorw ("Error" ,
365+ "err" , err ,
366+ )
367+ }
368+ return err
369+ }
370+
342371 switch cmd {
343372 case editConfigCmd .FullCommand ():
344- return editConfig (configPath , * editConfigCmdEditorFlag )
373+ return logErrAndReturn ( editConfig (sugar , configPath , * editConfigCmdEditorFlag ) )
345374 case grabCmd .FullCommand ():
346375 if cfgErr != nil {
347- sugar .Errorw (
348- "Config error: maybe try `edit-config`" ,
349- "err" , cfgErr ,
350- )
351376 fmt .Fprintf (os .Stderr , "Config error: maybe try `edit-config`: %v\n " , cfgErr )
352- return errNotAtTheDisco
377+ logErrAndReturn ( cfgErr )
353378 }
354- return grab (subreddits )
379+ return logErrAndReturn ( grab (sugar , subreddits ) )
355380 }
356381
357382 return nil
358383}
359384
360385func main () {
361386 err := run ()
362- if err == errNotAtTheDisco {
363- // os.Exit(1)
364- } else if err != nil {
387+ if err != nil {
365388 panic (err )
366389 }
367390}
0 commit comments