diff --git a/server/internal/api/exports.go b/server/internal/api/exports.go index a45d445..9b1a026 100644 --- a/server/internal/api/exports.go +++ b/server/internal/api/exports.go @@ -343,8 +343,15 @@ func (a *ExportsAPI) create(w http.ResponseWriter, r *http.Request) { func (a *ExportsAPI) runJob(id uint64, db *sql.DB, q string, args []interface{}, fields []string, cols []string, fmt string) { defer func() { if r := recover(); r != nil { + logging.JSON("ERROR", map[string]interface{}{ + "event": "export_panic", + "job_id": id, + "error": utils.ToString(r), + "fields": fields, + "format": fmt, + }) + log.Printf("[EXPORT_FAILED] job_id=%d reason=panic error=%v fields=%v", id, r, fields) repo.NewExportRepo().MarkFailed(a.meta, id) - logging.JSON("ERROR", map[string]interface{}{"event": "export_panic", "job_id": id, "error": utils.ToString(r)}) } }() // load datasource once for transform decisions @@ -442,6 +449,16 @@ func (a *ExportsAPI) runJob(id uint64, db *sql.DB, q string, args []interface{}, } cnt, _, e := rrepo.StreamCursor(db, cq, cargs, cur, batch, cols, newWriter, transform, constants.ExportThresholds.MaxRowsPerFile, onRoll, onProgress) if e != nil { + logging.JSON("ERROR", map[string]interface{}{ + "event": "export_stream_error", + "job_id": id, + "stage": "csv_chunk", + "error": e.Error(), + "datasource": jobDS, + "sql": cq, + "args": cargs, + }) + log.Printf("[EXPORT_FAILED] job_id=%d stage=csv_chunk error=%v sql=%s", id, e, cq) rrepo.MarkFailed(a.meta, id) return } @@ -486,6 +503,16 @@ func (a *ExportsAPI) runJob(id uint64, db *sql.DB, q string, args []interface{}, } count, files2, err := rrepo.StreamCursor(db, q, args, cur, batch, cols, newWriter, transform, constants.ExportThresholds.MaxRowsPerFile, onRoll, onProgress) if err != nil { + logging.JSON("ERROR", map[string]interface{}{ + "event": "export_stream_error", + "job_id": id, + "stage": "xlsx_direct", + "error": err.Error(), + "datasource": jobDS, + "fields": fields, + "sql": q, + }) + log.Printf("[EXPORT_FAILED] job_id=%d stage=xlsx_direct error=%v fields_count=%d", id, err, len(fields)) rrepo.MarkFailed(a.meta, id) return }