1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #if !defined(DLL_EXPORT) && !defined(USE_STATIC_LIB)
20 #  define USE_STATIC_LIB
21 #endif
22 
23 #include "zookeeper_log.h"
24 #ifndef WIN32
25 #include <unistd.h>
26 #else
27 typedef DWORD pid_t;
28 #include <process.h> /* for getpid */
29 #endif
30 
31 #include <stdarg.h>
32 #include <time.h>
33 
34 #define TIME_NOW_BUF_SIZE 1024
35 #define FORMAT_LOG_BUF_SIZE 4096
36 
37 #ifdef THREADED
38 #ifndef WIN32
39 #include <pthread.h>
40 #else
41 #include "winport.h"
42 #endif
43 
44 static pthread_key_t time_now_buffer;
45 static pthread_key_t format_log_msg_buffer;
46 
freeBuffer(void * p)47 void freeBuffer(void* p){
48     if(p) free(p);
49 }
50 
prepareTSDKeys()51 __attribute__((constructor)) void prepareTSDKeys() {
52     pthread_key_create (&time_now_buffer, freeBuffer);
53     pthread_key_create (&format_log_msg_buffer, freeBuffer);
54 }
55 
getTSData(pthread_key_t key,int size)56 char* getTSData(pthread_key_t key,int size){
57     char* p=pthread_getspecific(key);
58     if(p==0){
59         int res;
60         p=calloc(1,size);
61         res=pthread_setspecific(key,p);
62         if(res!=0){
63             fprintf(stderr,"Failed to set TSD key: %d",res);
64         }
65     }
66     return p;
67 }
68 
get_time_buffer()69 char* get_time_buffer(){
70     return getTSData(time_now_buffer,TIME_NOW_BUF_SIZE);
71 }
72 
get_format_log_buffer()73 char* get_format_log_buffer(){
74     return getTSData(format_log_msg_buffer,FORMAT_LOG_BUF_SIZE);
75 }
76 #else
get_time_buffer()77 char* get_time_buffer(){
78     static char buf[TIME_NOW_BUF_SIZE];
79     return buf;
80 }
81 
get_format_log_buffer()82 char* get_format_log_buffer(){
83     static char buf[FORMAT_LOG_BUF_SIZE];
84     return buf;
85 }
86 
87 #endif
88 
89 ZooLogLevel logLevel=ZOO_LOG_LEVEL_INFO;
90 
91 static FILE* logStream=0;
zoo_get_log_stream()92 FILE* zoo_get_log_stream(){
93     if(logStream==0)
94         logStream=stderr;
95     return logStream;
96 }
97 
zoo_set_log_stream(FILE * stream)98 void zoo_set_log_stream(FILE* stream){
99     logStream=stream;
100 }
101 
time_now(char * now_str)102 static const char* time_now(char* now_str){
103     struct timeval tv;
104     struct tm lt;
105     time_t now = 0;
106     size_t len = 0;
107 
108     gettimeofday(&tv,0);
109 
110     now = tv.tv_sec;
111     localtime_r(&now, &lt);
112 
113     // clone the format used by log4j ISO8601DateFormat
114     // specifically: "yyyy-MM-dd HH:mm:ss,SSS"
115 
116     len = strftime(now_str, TIME_NOW_BUF_SIZE,
117                           "%Y-%m-%d %H:%M:%S",
118                           &lt);
119 
120     len += snprintf(now_str + len,
121                     TIME_NOW_BUF_SIZE - len,
122                     ",%03d",
123                     (int)(tv.tv_usec/1000));
124 
125     return now_str;
126 }
127 
log_message(log_callback_fn callback,ZooLogLevel curLevel,int line,const char * funcName,const char * format,...)128 void log_message(log_callback_fn callback, ZooLogLevel curLevel,
129     int line, const char* funcName, const char* format, ...)
130 {
131     static const char* dbgLevelStr[]={"ZOO_INVALID","ZOO_ERROR","ZOO_WARN",
132             "ZOO_INFO","ZOO_DEBUG"};
133     static pid_t pid=0;
134     va_list va;
135     int ofs = 0;
136 #ifdef THREADED
137     unsigned long int tid = 0;
138 #endif
139 #ifdef WIN32
140     char timebuf [TIME_NOW_BUF_SIZE];
141     const char* time = time_now(timebuf);
142 #else
143     const char* time = time_now(get_time_buffer());
144 #endif
145 
146     char* buf = get_format_log_buffer();
147     if(!buf)
148     {
149         fprintf(stderr, "log_message: Unable to allocate memory buffer");
150         return;
151     }
152 
153     if(pid==0)
154     {
155         pid=getpid();
156     }
157 
158 
159 #ifndef THREADED
160 
161     // pid_t is long on Solaris
162     ofs = snprintf(buf, FORMAT_LOG_BUF_SIZE,
163                    "%s:%ld:%s@%s@%d: ", time, (long)pid,
164                    dbgLevelStr[curLevel], funcName, line);
165 #else
166 
167     #ifdef WIN32
168         tid = (unsigned long int)(pthread_self().thread_id);
169     #else
170         tid = (unsigned long int)(pthread_self());
171     #endif
172 
173     ofs = snprintf(buf, FORMAT_LOG_BUF_SIZE-1,
174                    "%s:%ld(0x%lx):%s@%s@%d: ", time, (long)pid, tid,
175                    dbgLevelStr[curLevel], funcName, line);
176 #endif
177 
178     // Now grab the actual message out of the variadic arg list
179     va_start(va, format);
180     vsnprintf(buf+ofs, FORMAT_LOG_BUF_SIZE-1-ofs, format, va);
181     va_end(va);
182 
183     if (callback)
184     {
185         callback(buf);
186     } else {
187         fprintf(zoo_get_log_stream(), "%s\n", buf);
188         fflush(zoo_get_log_stream());
189     }
190 }
191 
zoo_set_debug_level(ZooLogLevel level)192 void zoo_set_debug_level(ZooLogLevel level)
193 {
194     if(level==0){
195         // disable logging (unit tests do this)
196         logLevel=(ZooLogLevel)0;
197         return;
198     }
199     if(level<ZOO_LOG_LEVEL_ERROR)level=ZOO_LOG_LEVEL_ERROR;
200     if(level>ZOO_LOG_LEVEL_DEBUG)level=ZOO_LOG_LEVEL_DEBUG;
201     logLevel=level;
202 }
203 
204