1// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// 1. Redistributions of source code must retain the above copyright notice, this
9//    list of conditions and the following disclaimer.
10// 2. Redistributions in binary form must reproduce the above copyright notice,
11//    this list of conditions and the following disclaimer in the documentation
12//    and/or other materials provided with the distribution.
13//
14// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25package seelog
26
27import (
28	"errors"
29	"fmt"
30	"io"
31)
32
33// A dispatcherInterface is used to dispatch message to all underlying receivers.
34// Dispatch logic depends on given context and log level. Any errors are reported using errorFunc.
35// Also, as underlying receivers may have a state, dispatcher has a ShuttingDown method which performs
36// an immediate cleanup of all data that is stored in the receivers
37type dispatcherInterface interface {
38	flusherInterface
39	io.Closer
40	Dispatch(message string, level LogLevel, context LogContextInterface, errorFunc func(err error))
41}
42
43type dispatcher struct {
44	formatter   *formatter
45	writers     []*formattedWriter
46	dispatchers []dispatcherInterface
47}
48
49// Creates a dispatcher which dispatches data to a list of receivers.
50// Each receiver should be either a Dispatcher or io.Writer, otherwise an error will be returned
51func createDispatcher(formatter *formatter, receivers []interface{}) (*dispatcher, error) {
52	if formatter == nil {
53		return nil, errors.New("formatter cannot be nil")
54	}
55	if receivers == nil || len(receivers) == 0 {
56		return nil, errors.New("receivers cannot be nil or empty")
57	}
58
59	disp := &dispatcher{formatter, make([]*formattedWriter, 0), make([]dispatcherInterface, 0)}
60	for _, receiver := range receivers {
61		writer, ok := receiver.(*formattedWriter)
62		if ok {
63			disp.writers = append(disp.writers, writer)
64			continue
65		}
66
67		ioWriter, ok := receiver.(io.Writer)
68		if ok {
69			writer, err := NewFormattedWriter(ioWriter, disp.formatter)
70			if err != nil {
71				return nil, err
72			}
73			disp.writers = append(disp.writers, writer)
74			continue
75		}
76
77		dispInterface, ok := receiver.(dispatcherInterface)
78		if ok {
79			disp.dispatchers = append(disp.dispatchers, dispInterface)
80			continue
81		}
82
83		return nil, errors.New("method can receive either io.Writer or dispatcherInterface")
84	}
85
86	return disp, nil
87}
88
89func (disp *dispatcher) Dispatch(
90	message string,
91	level LogLevel,
92	context LogContextInterface,
93	errorFunc func(err error)) {
94
95	for _, writer := range disp.writers {
96		err := writer.Write(message, level, context)
97		if err != nil {
98			errorFunc(err)
99		}
100	}
101
102	for _, dispInterface := range disp.dispatchers {
103		dispInterface.Dispatch(message, level, context, errorFunc)
104	}
105}
106
107// Flush goes through all underlying writers which implement flusherInterface interface
108// and closes them. Recursively performs the same action for underlying dispatchers
109func (disp *dispatcher) Flush() {
110	for _, disp := range disp.Dispatchers() {
111		disp.Flush()
112	}
113
114	for _, formatWriter := range disp.Writers() {
115		flusher, ok := formatWriter.Writer().(flusherInterface)
116		if ok {
117			flusher.Flush()
118		}
119	}
120}
121
122// Close goes through all underlying writers which implement io.Closer interface
123// and closes them. Recursively performs the same action for underlying dispatchers
124// Before closing, writers are flushed to prevent loss of any buffered data, so
125// a call to Flush() func before Close() is not necessary
126func (disp *dispatcher) Close() error {
127	for _, disp := range disp.Dispatchers() {
128		disp.Flush()
129		err := disp.Close()
130		if err != nil {
131			return err
132		}
133	}
134
135	for _, formatWriter := range disp.Writers() {
136		flusher, ok := formatWriter.Writer().(flusherInterface)
137		if ok {
138			flusher.Flush()
139		}
140
141		closer, ok := formatWriter.Writer().(io.Closer)
142		if ok {
143			err := closer.Close()
144			if err != nil {
145				return err
146			}
147		}
148	}
149
150	return nil
151}
152
153func (disp *dispatcher) Writers() []*formattedWriter {
154	return disp.writers
155}
156
157func (disp *dispatcher) Dispatchers() []dispatcherInterface {
158	return disp.dispatchers
159}
160
161func (disp *dispatcher) String() string {
162	str := "formatter: " + disp.formatter.String() + "\n"
163
164	str += "    ->Dispatchers:"
165
166	if len(disp.dispatchers) == 0 {
167		str += "none\n"
168	} else {
169		str += "\n"
170
171		for _, disp := range disp.dispatchers {
172			str += fmt.Sprintf("        ->%s", disp)
173		}
174	}
175
176	str += "    ->Writers:"
177
178	if len(disp.writers) == 0 {
179		str += "none\n"
180	} else {
181		str += "\n"
182
183		for _, writer := range disp.writers {
184			str += fmt.Sprintf("        ->%s\n", writer)
185		}
186	}
187
188	return str
189}
190