@@ -275,31 +275,39 @@ type WriteError struct {
275275
276276 Code int
277277 Message string
278+ Details bson.Raw
278279}
279280
280- func (we WriteError ) Error () string { return we .Message }
281+ func (we WriteError ) Error () string {
282+ msg := we .Message
283+ if len (we .Details ) > 0 {
284+ msg = fmt .Sprintf ("%s: %s" , msg , we .Details .String ())
285+ }
286+ return msg
287+ }
281288
282289// WriteErrors is a group of write errors that occurred during execution of a write operation.
283290type WriteErrors []WriteError
284291
285292// Error implements the error interface.
286293func (we WriteErrors ) Error () string {
287- var buf bytes.Buffer
288- fmt .Fprint (& buf , "write errors: [" )
289- for idx , err := range we {
290- if idx != 0 {
291- fmt .Fprintf (& buf , ", " )
292- }
293- fmt .Fprintf (& buf , "{%s}" , err )
294+ errs := make ([]error , len (we ))
295+ for i := 0 ; i < len (we ); i ++ {
296+ errs [i ] = we [i ]
294297 }
295- fmt . Fprint ( & buf , "]" )
296- return buf . String ( )
298+ // WriteErrors isn't returned from batch operations, but we can still use the same formatter.
299+ return "write errors: " + joinBatchErrors ( errs )
297300}
298301
299302func writeErrorsFromDriverWriteErrors (errs driver.WriteErrors ) WriteErrors {
300303 wes := make (WriteErrors , 0 , len (errs ))
301304 for _ , err := range errs {
302- wes = append (wes , WriteError {Index : int (err .Index ), Code : int (err .Code ), Message : err .Message })
305+ wes = append (wes , WriteError {
306+ Index : int (err .Index ),
307+ Code : int (err .Code ),
308+ Message : err .Message ,
309+ Details : bson .Raw (err .Details ),
310+ })
303311 }
304312 return wes
305313}
@@ -336,11 +344,21 @@ type WriteException struct {
336344
337345// Error implements the error interface.
338346func (mwe WriteException ) Error () string {
339- var buf bytes.Buffer
340- fmt .Fprint (& buf , "multiple write errors: [" )
341- fmt .Fprintf (& buf , "{%s}, " , mwe .WriteErrors )
342- fmt .Fprintf (& buf , "{%s}]" , mwe .WriteConcernError )
343- return buf .String ()
347+ causes := make ([]string , 0 , 2 )
348+ if mwe .WriteConcernError != nil {
349+ causes = append (causes , "write concern error: " + mwe .WriteConcernError .Error ())
350+ }
351+ if len (mwe .WriteErrors ) > 0 {
352+ // The WriteErrors error message already starts with "write errors:", so don't add it to the
353+ // error message again.
354+ causes = append (causes , mwe .WriteErrors .Error ())
355+ }
356+
357+ message := "write exception: "
358+ if len (causes ) == 0 {
359+ return message + "no causes"
360+ }
361+ return message + strings .Join (causes , ", " )
344362}
345363
346364// HasErrorCode returns true if the error has the specified code.
@@ -420,9 +438,7 @@ type BulkWriteError struct {
420438
421439// Error implements the error interface.
422440func (bwe BulkWriteError ) Error () string {
423- var buf bytes.Buffer
424- fmt .Fprintf (& buf , "{%s}" , bwe .WriteError )
425- return buf .String ()
441+ return bwe .WriteError .Error ()
426442}
427443
428444// BulkWriteException is the error type returned by BulkWrite and InsertMany operations.
@@ -439,11 +455,23 @@ type BulkWriteException struct {
439455
440456// Error implements the error interface.
441457func (bwe BulkWriteException ) Error () string {
442- var buf bytes.Buffer
443- fmt .Fprint (& buf , "bulk write error: [" )
444- fmt .Fprintf (& buf , "{%s}, " , bwe .WriteErrors )
445- fmt .Fprintf (& buf , "{%s}]" , bwe .WriteConcernError )
446- return buf .String ()
458+ causes := make ([]string , 0 , 2 )
459+ if bwe .WriteConcernError != nil {
460+ causes = append (causes , "write concern error: " + bwe .WriteConcernError .Error ())
461+ }
462+ if len (bwe .WriteErrors ) > 0 {
463+ errs := make ([]error , len (bwe .WriteErrors ))
464+ for i := 0 ; i < len (bwe .WriteErrors ); i ++ {
465+ errs [i ] = & bwe .WriteErrors [i ]
466+ }
467+ causes = append (causes , "write errors: " + joinBatchErrors (errs ))
468+ }
469+
470+ message := "bulk write exception: "
471+ if len (causes ) == 0 {
472+ return message + "no causes"
473+ }
474+ return "bulk write exception: " + strings .Join (causes , ", " )
447475}
448476
449477// HasErrorCode returns true if any of the errors have the specified code.
@@ -539,3 +567,34 @@ func processWriteError(err error) (returnResult, error) {
539567 return rrAll , nil
540568 }
541569}
570+
571+ // batchErrorsTargetLength is the target length of error messages returned by batch operation
572+ // error types. Try to limit batch error messages to 2kb to prevent problems when printing error
573+ // messages from large batch operations.
574+ const batchErrorsTargetLength = 2000
575+
576+ // joinBatchErrors appends messages from the given errors to a comma-separated string. If the
577+ // string exceeds 2kb, it stops appending error messages and appends the message "+N more errors..."
578+ // to the end.
579+ //
580+ // Example format:
581+ // "[message 1, message 2, +8 more errors...]"
582+ func joinBatchErrors (errs []error ) string {
583+ var buf bytes.Buffer
584+ fmt .Fprint (& buf , "[" )
585+ for idx , err := range errs {
586+ if idx != 0 {
587+ fmt .Fprint (& buf , ", " )
588+ }
589+ // If the error message has exceeded the target error message length, stop appending errors
590+ // to the message and append the number of remaining errors instead.
591+ if buf .Len () > batchErrorsTargetLength {
592+ fmt .Fprintf (& buf , "+%d more errors..." , len (errs )- idx )
593+ break
594+ }
595+ fmt .Fprint (& buf , err .Error ())
596+ }
597+ fmt .Fprint (& buf , "]" )
598+
599+ return buf .String ()
600+ }
0 commit comments