Path: blob/dev/pkg/reporting/exporters/jsonl/jsonl.go
2070 views
package jsonl12import (3"os"4"sync"56"github.com/pkg/errors"7"github.com/projectdiscovery/nuclei/v3/pkg/output"8"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"9)1011type Exporter struct {12options *Options13mutex *sync.Mutex14rows []output.ResultEvent15outputFile *os.File16}1718// Options contains the configuration options for JSONL exporter client19type Options struct {20// File is the file to export found JSONL result to21File string `yaml:"file"`22// OmitRaw whether to exclude the raw request and response from the output23OmitRaw bool `yaml:"omit-raw"`24// BatchSize the number of records to keep in memory before writing them out to the JSONL file or 0 to disable25// batching (default)26BatchSize int `yaml:"batch-size"`27}2829// New creates a new JSONL exporter integration client based on options.30func New(options *Options) (*Exporter, error) {31exporter := &Exporter{32mutex: &sync.Mutex{},33options: options,34rows: []output.ResultEvent{},35}36return exporter, nil37}3839// Export appends the passed result event to the list of objects to be exported to the resulting JSONL file40func (exporter *Exporter) Export(event *output.ResultEvent) error {41exporter.mutex.Lock()42defer exporter.mutex.Unlock()4344if exporter.options.OmitRaw {45event.Request = ""46event.Response = ""47}4849// Add the event to the rows50exporter.rows = append(exporter.rows, *event)5152// If the batch size is greater than 0 and the number of rows has reached the batch, flush it to the database53if exporter.options.BatchSize > 0 && len(exporter.rows) >= exporter.options.BatchSize {54err := exporter.WriteRows()55if err != nil {56// The error is already logged, return it to bubble up to the caller57return err58}59}6061return nil62}6364// WriteRows writes all rows from the rows list to JSONL file and removes them from the list65func (exporter *Exporter) WriteRows() error {66// Open the file for writing if it's not already.67// This will recreate the file if it exists, but keep the file handle so that batched writes within the same68// execution are appended to the same file.69var err error70if exporter.outputFile == nil {71// Open the JSONL file for writing and create it if it doesn't exist72exporter.outputFile, err = os.OpenFile(exporter.options.File, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)73if err != nil {74return errors.Wrap(err, "failed to create JSONL file")75}76}7778// Loop through the rows and write them, removing them as they're entered79for len(exporter.rows) > 0 {80row := exporter.rows[0]8182// Convert the row to JSON byte array and append a trailing newline. This is treated as a single line in JSONL83obj, err := json.Marshal(row)84if err != nil {85return errors.Wrap(err, "failed to generate row for JSONL report")86}8788obj = append(obj, '\n')8990// Attempt to append the JSON line to file specified in options.JSONLExport91if _, err = exporter.outputFile.Write(obj); err != nil {92return errors.Wrap(err, "failed to append JSONL line")93}9495// Remove the item from the list96exporter.rows = exporter.rows[1:]97}9899return nil100}101102// Close writes the in-memory data to the JSONL file specified by options.JSONLExport and closes the exporter after103// operation104func (exporter *Exporter) Close() error {105exporter.mutex.Lock()106defer exporter.mutex.Unlock()107108// Write any remaining rows to the file109// Write all pending rows110err := exporter.WriteRows()111if err != nil {112// The error is already logged, return it to bubble up to the caller113return err114}115116// Close the file117if err := exporter.outputFile.Close(); err != nil {118return errors.Wrap(err, "failed to close JSONL file")119}120121return nil122}123124125