TextOutput.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /*
  2. * Copyright (c) 2014 by Farsight Security, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package dnstap
  17. import (
  18. "bufio"
  19. "io"
  20. "os"
  21. "google.golang.org/protobuf/proto"
  22. )
  23. // A TextFormatFunc renders a dnstap message into a human readable format.
  24. type TextFormatFunc func(*Dnstap) ([]byte, bool)
  25. // TextOutput implements a dnstap Output rendering dnstap data as text.
  26. type TextOutput struct {
  27. format TextFormatFunc
  28. outputChannel chan []byte
  29. wait chan bool
  30. writer *bufio.Writer
  31. log Logger
  32. }
  33. // NewTextOutput creates a TextOutput writing dnstap data to the given io.Writer
  34. // in the text format given by the TextFormatFunc format.
  35. func NewTextOutput(writer io.Writer, format TextFormatFunc) (o *TextOutput) {
  36. o = new(TextOutput)
  37. o.format = format
  38. o.outputChannel = make(chan []byte, outputChannelSize)
  39. o.writer = bufio.NewWriter(writer)
  40. o.wait = make(chan bool)
  41. return
  42. }
  43. // NewTextOutputFromFilename creates a TextOutput writing dnstap data to a
  44. // file with the given filename in the format given by format. If doAppend
  45. // is false, the file is truncated if it already exists, otherwise the file
  46. // is opened for appending.
  47. func NewTextOutputFromFilename(fname string, format TextFormatFunc, doAppend bool) (o *TextOutput, err error) {
  48. if fname == "" || fname == "-" {
  49. return NewTextOutput(os.Stdout, format), nil
  50. }
  51. var writer io.Writer
  52. if doAppend {
  53. writer, err = os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
  54. } else {
  55. writer, err = os.Create(fname)
  56. }
  57. if err != nil {
  58. return
  59. }
  60. return NewTextOutput(writer, format), nil
  61. }
  62. // SetLogger configures a logger for error events in the TextOutput
  63. func (o *TextOutput) SetLogger(logger Logger) {
  64. o.log = logger
  65. }
  66. // GetOutputChannel returns the channel on which the TextOutput accepts dnstap data.
  67. //
  68. // GetOutputChannel satisfies the dnstap Output interface.
  69. func (o *TextOutput) GetOutputChannel() chan []byte {
  70. return o.outputChannel
  71. }
  72. // RunOutputLoop receives dnstap data sent on the output channel, formats it
  73. // with the configured TextFormatFunc, and writes it to the file or io.Writer
  74. // of the TextOutput.
  75. //
  76. // RunOutputLoop satisfies the dnstap Output interface.
  77. func (o *TextOutput) RunOutputLoop() {
  78. dt := &Dnstap{}
  79. for frame := range o.outputChannel {
  80. if err := proto.Unmarshal(frame, dt); err != nil {
  81. o.log.Printf("dnstap.TextOutput: proto.Unmarshal() failed: %s, returning", err)
  82. break
  83. }
  84. buf, ok := o.format(dt)
  85. if !ok {
  86. o.log.Printf("dnstap.TextOutput: text format function failed, returning")
  87. break
  88. }
  89. if _, err := o.writer.Write(buf); err != nil {
  90. o.log.Printf("dnstap.TextOutput: write error: %v, returning", err)
  91. break
  92. }
  93. o.writer.Flush()
  94. }
  95. close(o.wait)
  96. }
  97. // Close closes the output channel and returns when all pending data has been
  98. // written.
  99. //
  100. // Close satisfies the dnstap Output interface.
  101. func (o *TextOutput) Close() {
  102. close(o.outputChannel)
  103. <-o.wait
  104. o.writer.Flush()
  105. }