github.com/observiq/carbon@v0.9.11-0.20200820160507-1b872e368a5e/operator/builtin/output/file.go (about)

     1  package output
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"html/template"
     8  	"os"
     9  	"sync"
    10  
    11  	"github.com/observiq/carbon/entry"
    12  	"github.com/observiq/carbon/operator"
    13  	"github.com/observiq/carbon/operator/helper"
    14  )
    15  
    16  func init() {
    17  	operator.Register("file_output", func() operator.Builder { return NewFileOutputConfig("") })
    18  }
    19  
    20  func NewFileOutputConfig(operatorID string) *FileOutputConfig {
    21  	return &FileOutputConfig{
    22  		OutputConfig: helper.NewOutputConfig(operatorID, "file_output"),
    23  	}
    24  }
    25  
    26  // FileOutputConfig is the configuration of a file output operatorn.
    27  type FileOutputConfig struct {
    28  	helper.OutputConfig `yaml:",inline"`
    29  
    30  	Path   string `json:"path" yaml:"path"`
    31  	Format string `json:"format,omitempty" path:"format,omitempty"`
    32  }
    33  
    34  // Build will build a file output operator.
    35  func (c FileOutputConfig) Build(context operator.BuildContext) (operator.Operator, error) {
    36  	outputOperator, err := c.OutputConfig.Build(context)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	var tmpl *template.Template
    42  	if c.Format != "" {
    43  		tmpl, err = template.New("file").Parse(c.Format)
    44  		if err != nil {
    45  			return nil, err
    46  		}
    47  	}
    48  
    49  	if c.Path == "" {
    50  		return nil, fmt.Errorf("must provide a path to output to")
    51  	}
    52  
    53  	fileOutput := &FileOutput{
    54  		OutputOperator: outputOperator,
    55  		path:           c.Path,
    56  		tmpl:           tmpl,
    57  	}
    58  
    59  	return fileOutput, nil
    60  }
    61  
    62  // FileOutput is an operator that writes logs to a file.
    63  type FileOutput struct {
    64  	helper.OutputOperator
    65  
    66  	path    string
    67  	tmpl    *template.Template
    68  	encoder *json.Encoder
    69  	file    *os.File
    70  	mux     sync.Mutex
    71  }
    72  
    73  // Start will open the output file.
    74  func (fo *FileOutput) Start() error {
    75  	var err error
    76  	fo.file, err = os.OpenFile(fo.path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	fo.encoder = json.NewEncoder(fo.file)
    82  
    83  	return nil
    84  }
    85  
    86  // Stop will close the output file.
    87  func (fo *FileOutput) Stop() error {
    88  	if fo.file != nil {
    89  		fo.file.Close()
    90  	}
    91  	return nil
    92  }
    93  
    94  // Process will write an entry to the output file.
    95  func (fo *FileOutput) Process(ctx context.Context, entry *entry.Entry) error {
    96  	fo.mux.Lock()
    97  	defer fo.mux.Unlock()
    98  
    99  	if fo.tmpl != nil {
   100  		err := fo.tmpl.Execute(fo.file, entry)
   101  		if err != nil {
   102  			return err
   103  		}
   104  	} else {
   105  		err := fo.encoder.Encode(entry)
   106  		if err != nil {
   107  			return err
   108  		}
   109  	}
   110  
   111  	return nil
   112  }