@@ -59,6 +59,8 @@ type BulkOptions struct {
5959 ConnStr string
6060 HttpAddr string
6161 IgnoreErrors bool
62+ LogErrors bool
63+ ErrorLogPath string
6264 CustomTokenizers string
6365 NewUids bool
6466 ClientDir string
@@ -79,18 +81,59 @@ type BulkOptions struct {
7981 Badger badger.Options
8082}
8183
84+ // chunkWithMeta wraps a chunk buffer with metadata about the source file.
85+ type chunkWithMeta struct {
86+ buf * bytes.Buffer
87+ filename string
88+ }
89+
8290type state struct {
8391 opt * BulkOptions
8492 prog * progress
8593 xids * xidmap.XidMap
8694 schema * schemaStore
8795 shards * shardMap
88- readerChunkCh chan * bytes. Buffer
96+ readerChunkCh chan * chunkWithMeta
8997 mapFileId uint32 // Used atomically to name the output files of the mappers.
9098 dbs []* badger.DB
9199 tmpDbs []* badger.DB // Temporary DB to write the split lists to avoid ordering issues.
92100 writeTs uint64 // All badger writes use this timestamp
93101 namespaces * sync.Map // To store the encountered namespaces.
102+ errorLog * errorLogger // Error logger for --log_errors
103+ }
104+
105+ // errorLogger provides thread-safe logging of parsing errors to a file.
106+ type errorLogger struct {
107+ mu sync.Mutex
108+ file * os.File
109+ }
110+
111+ func newErrorLogger (path string ) (* errorLogger , error ) {
112+ f , err := os .OpenFile (path , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , 0644 )
113+ if err != nil {
114+ return nil , err
115+ }
116+ return & errorLogger {file : f }, nil
117+ }
118+
119+ func (e * errorLogger ) Log (filename string , err error , extra string ) {
120+ if e == nil || e .file == nil {
121+ return
122+ }
123+ e .mu .Lock ()
124+ defer e .mu .Unlock ()
125+ // Truncate very long extra info in the log
126+ if len (extra ) > 500 {
127+ extra = extra [:500 ] + "..."
128+ }
129+ fmt .Fprintf (e .file , "file: %s\n error: %v\n %s\n " , filename , err , extra )
130+ }
131+
132+ func (e * errorLogger ) Close () error {
133+ if e == nil || e .file == nil {
134+ return nil
135+ }
136+ return e .file .Close ()
94137}
95138
96139type loader struct {
@@ -130,14 +173,26 @@ func newLoader(opt *BulkOptions) *loader {
130173 x .Checkf (err , "Unable to connect to alpha, Is it running at %s?" , opt .ConnStr )
131174 }
132175
176+ var errLog * errorLogger
177+ if opt .LogErrors {
178+ if ! opt .IgnoreErrors {
179+ fmt .Fprintln (os .Stderr , "Warning: --log_errors requires --ignore_errors to be set" )
180+ }
181+ var err error
182+ errLog , err = newErrorLogger (opt .ErrorLogPath )
183+ x .Checkf (err , "Unable to create error log file at %s" , opt .ErrorLogPath )
184+ fmt .Printf ("Error logging enabled, writing to: %s\n " , opt .ErrorLogPath )
185+ }
186+
133187 st := & state {
134188 opt : opt ,
135189 prog : newProgress (),
136190 shards : newShardMap (opt .MapShards ),
137191 // Lots of gz readers, so not much channel buffer needed.
138- readerChunkCh : make (chan * bytes. Buffer , opt .NumGoroutines ),
192+ readerChunkCh : make (chan * chunkWithMeta , opt .NumGoroutines ),
139193 writeTs : getWriteTimestamp (zero , dg ),
140194 namespaces : & sync.Map {},
195+ errorLog : errLog ,
141196 }
142197 st .schema = newSchemaStore (readSchema (opt ), opt , st )
143198 ld := & loader {
@@ -348,7 +403,7 @@ func (ld *loader) mapStage() {
348403 for {
349404 chunkBuf , err := chunk .Chunk (r )
350405 if chunkBuf != nil && chunkBuf .Len () > 0 {
351- ld .readerChunkCh <- chunkBuf
406+ ld .readerChunkCh <- & chunkWithMeta { buf : chunkBuf , filename : file }
352407 }
353408 if err == io .EOF {
354409 break
@@ -453,7 +508,7 @@ func (ld *loader) processGqlSchema(loadType chunker.InputFormat) {
453508 _ , err := fmt .Fprintf (gqlBuf , jsonSchema , ns , schema )
454509 x .Check (err )
455510 }
456- ld .readerChunkCh <- gqlBuf
511+ ld .readerChunkCh <- & chunkWithMeta { buf : gqlBuf , filename : "<gql_schema>" }
457512 }
458513
459514 buf := readGqlSchema (ld .opt )
@@ -531,5 +586,10 @@ func (ld *loader) cleanup() {
531586 x .Check (db .Close ())
532587 x .Check (os .RemoveAll (opts .Dir ))
533588 }
589+ if ld .errorLog != nil {
590+ if err := ld .errorLog .Close (); err != nil {
591+ glog .Warningf ("error closing error log: %v" , err )
592+ }
593+ }
534594 ld .prog .endSummary ()
535595}
0 commit comments