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	"container/list"
29	"fmt"
30	"sync"
31)
32
33// MaxQueueSize is the critical number of messages in the queue that result in an immediate flush.
34const (
35	MaxQueueSize = 10000
36)
37
38type msgQueueItem struct {
39	level   LogLevel
40	context LogContextInterface
41	message fmt.Stringer
42}
43
44// asyncLogger represents common data for all asynchronous loggers
45type asyncLogger struct {
46	commonLogger
47	msgQueue         *list.List
48	queueHasElements *sync.Cond
49}
50
51// newAsyncLogger creates a new asynchronous logger
52func newAsyncLogger(config *logConfig) *asyncLogger {
53	asnLogger := new(asyncLogger)
54
55	asnLogger.msgQueue = list.New()
56	asnLogger.queueHasElements = sync.NewCond(new(sync.Mutex))
57
58	asnLogger.commonLogger = *newCommonLogger(config, asnLogger)
59
60	return asnLogger
61}
62
63func (asnLogger *asyncLogger) innerLog(
64	level LogLevel,
65	context LogContextInterface,
66	message fmt.Stringer) {
67
68	asnLogger.addMsgToQueue(level, context, message)
69}
70
71func (asnLogger *asyncLogger) Close() {
72	asnLogger.m.Lock()
73	defer asnLogger.m.Unlock()
74
75	if !asnLogger.Closed() {
76		asnLogger.flushQueue(true)
77		asnLogger.config.RootDispatcher.Flush()
78
79		if err := asnLogger.config.RootDispatcher.Close(); err != nil {
80			reportInternalError(err)
81		}
82
83		asnLogger.closedM.Lock()
84		asnLogger.closed = true
85		asnLogger.closedM.Unlock()
86		asnLogger.queueHasElements.Broadcast()
87	}
88}
89
90func (asnLogger *asyncLogger) Flush() {
91	asnLogger.m.Lock()
92	defer asnLogger.m.Unlock()
93
94	if !asnLogger.Closed() {
95		asnLogger.flushQueue(true)
96		asnLogger.config.RootDispatcher.Flush()
97	}
98}
99
100func (asnLogger *asyncLogger) flushQueue(lockNeeded bool) {
101	if lockNeeded {
102		asnLogger.queueHasElements.L.Lock()
103		defer asnLogger.queueHasElements.L.Unlock()
104	}
105
106	for asnLogger.msgQueue.Len() > 0 {
107		asnLogger.processQueueElement()
108	}
109}
110
111func (asnLogger *asyncLogger) processQueueElement() {
112	if asnLogger.msgQueue.Len() > 0 {
113		backElement := asnLogger.msgQueue.Front()
114		msg, _ := backElement.Value.(msgQueueItem)
115		asnLogger.processLogMsg(msg.level, msg.message, msg.context)
116		asnLogger.msgQueue.Remove(backElement)
117	}
118}
119
120func (asnLogger *asyncLogger) addMsgToQueue(
121	level LogLevel,
122	context LogContextInterface,
123	message fmt.Stringer) {
124
125	if !asnLogger.Closed() {
126		asnLogger.queueHasElements.L.Lock()
127		defer asnLogger.queueHasElements.L.Unlock()
128
129		if asnLogger.msgQueue.Len() >= MaxQueueSize {
130			fmt.Printf("Seelog queue overflow: more than %v messages in the queue. Flushing.\n", MaxQueueSize)
131			asnLogger.flushQueue(false)
132		}
133
134		queueItem := msgQueueItem{level, context, message}
135
136		asnLogger.msgQueue.PushBack(queueItem)
137		asnLogger.queueHasElements.Broadcast()
138	} else {
139		err := fmt.Errorf("queue closed! Cannot process element: %d %#v", level, message)
140		reportInternalError(err)
141	}
142}
143