Coverage Report

Created: 2025-07-11 07:00

/src/logging-log4cxx/src/main/cpp/threadspecificdata.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#include <log4cxx/log4cxx.h>
19
#include <log4cxx/logstring.h>
20
#include <log4cxx/helpers/threadspecificdata.h>
21
#include <log4cxx/helpers/exception.h>
22
#include <log4cxx/helpers/stringhelper.h>
23
#include <log4cxx/helpers/transcoder.h>
24
#include <apr_thread_proc.h>
25
#include <apr_strings.h>
26
#if !defined(LOG4CXX)
27
  #define LOG4CXX 1
28
#endif
29
#include <log4cxx/private/log4cxx_private.h>
30
#include <log4cxx/helpers/aprinitializer.h>
31
#include <sstream>
32
#include <algorithm>
33
#include <thread>
34
#include <mutex>
35
#include <list>
36
37
using namespace LOG4CXX_NS;
38
using namespace LOG4CXX_NS::helpers;
39
40
struct ThreadSpecificData::ThreadSpecificDataPrivate{
41
  ThreadSpecificDataPrivate()
42
1
    : pNamePair(std::make_shared<NamePair>())
43
1
  {
44
1
    setThreadIdName();
45
1
    setThreadUserName();
46
1
  }
47
  NDC::Stack ndcStack;
48
  MDC::Map mdcMap;
49
50
  std::shared_ptr<NamePair> pNamePair;
51
52
#if !LOG4CXX_LOGCHAR_IS_UNICHAR && !LOG4CXX_LOGCHAR_IS_WCHAR
53
  std::basic_ostringstream<logchar> logchar_stringstream;
54
#endif
55
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
56
  std::basic_ostringstream<wchar_t> wchar_stringstream;
57
#endif
58
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
59
  std::basic_ostringstream<UniChar> unichar_stringstream;
60
#endif
61
62
  void setThreadIdName();
63
  void setThreadUserName();
64
};
65
66
/* Generate an identifier for the current thread
67
*/
68
void ThreadSpecificData::ThreadSpecificDataPrivate::setThreadIdName()
69
1
{
70
1
#if LOG4CXX_HAS_PTHREAD_SELF && !(defined(_WIN32) && defined(_LIBCPP_VERSION))
71
  // pthread_t encoded in HEX takes needs as many characters
72
  // as two times the size of the type, plus an additional null byte.
73
1
  auto threadId = pthread_self();
74
1
  char result[sizeof(pthread_t) * 3 + 10];
75
1
  apr_snprintf(result, sizeof(result), LOG4CXX_APR_THREAD_FMTSPEC, (void*) &threadId);
76
1
  this->pNamePair->idString = Transcoder::decode(result);
77
#elif defined(_WIN32)
78
  char result[20];
79
  apr_snprintf(result, sizeof(result), LOG4CXX_WIN32_THREAD_FMTSPEC, GetCurrentThreadId());
80
  this->pNamePair->idString = Transcoder::decode(result);
81
#else
82
  std::stringstream ss;
83
  ss << std::hex << "0x" << std::this_thread::get_id();
84
  this->pNamePair->idString = Transcoder::decode(ss.str().c_str());
85
#endif
86
1
}
87
88
/*
89
 * Get the user-specified name of the current thread (on a per-platform basis).
90
 * This is set using a method such as pthread_setname_np on POSIX
91
 * systems or SetThreadDescription on Windows.
92
 */
93
void ThreadSpecificData::ThreadSpecificDataPrivate::setThreadUserName()
94
1
{
95
1
#if LOG4CXX_HAS_PTHREAD_GETNAME && !(defined(_WIN32) && defined(_LIBCPP_VERSION))
96
1
  char result[16];
97
1
  pthread_t current_thread = pthread_self();
98
1
  if (pthread_getname_np(current_thread, result, sizeof(result)) < 0 || 0 == result[0])
99
0
    this->pNamePair->threadName = this->pNamePair->idString;
100
1
  else
101
1
    this->pNamePair->threadName = Transcoder::decode(result);
102
#elif defined(_WIN32)
103
  typedef HRESULT (WINAPI *TGetThreadDescription)(HANDLE, PWSTR*);
104
  static struct initialiser
105
  {
106
    HMODULE hKernelBase;
107
    TGetThreadDescription GetThreadDescription;
108
    initialiser()
109
      : hKernelBase(GetModuleHandleA("KernelBase.dll"))
110
      , GetThreadDescription(nullptr)
111
    {
112
      if (hKernelBase)
113
        GetThreadDescription = reinterpret_cast<TGetThreadDescription>(GetProcAddress(hKernelBase, "GetThreadDescription"));
114
    }
115
  } win32func;
116
  if (win32func.GetThreadDescription)
117
  {
118
    PWSTR result = 0;
119
    HRESULT hr = win32func.GetThreadDescription(GetCurrentThread(), &result);
120
    if (SUCCEEDED(hr) && result)
121
    {
122
      std::wstring wresult = result;
123
      LOG4CXX_DECODE_WCHAR(decoded, wresult);
124
      LocalFree(result);
125
      this->pNamePair->threadName = decoded;
126
    }
127
  }
128
  if (this->pNamePair->threadName.empty())
129
    this->pNamePair->threadName = this->pNamePair->idString;
130
#else
131
  this->pNamePair->threadName = this->pNamePair->idString;
132
#endif
133
1
}
134
135
ThreadSpecificData::ThreadSpecificData()
136
1
  : m_priv(std::make_unique<ThreadSpecificDataPrivate>())
137
1
{
138
1
}
139
140
ThreadSpecificData::ThreadSpecificData(ThreadSpecificData&& other)
141
0
  : m_priv(std::move(other.m_priv))
142
0
{
143
0
}
144
145
ThreadSpecificData::~ThreadSpecificData()
146
1
{
147
1
  m_priv.reset();
148
1
}
149
150
NDC::Stack& ThreadSpecificData::getStack()
151
12.8k
{
152
12.8k
  return m_priv->ndcStack;
153
12.8k
}
154
155
MDC::Map& ThreadSpecificData::getMap()
156
8.40k
{
157
8.40k
  return m_priv->mdcMap;
158
8.40k
}
159
160
auto ThreadSpecificData::getNames() -> NamePairPtr
161
7.21k
{
162
7.21k
  auto p = getCurrentData();
163
7.21k
  return p ? p->m_priv->pNamePair : std::make_shared<NamePair>();
164
7.21k
}
165
166
#if !LOG4CXX_LOGCHAR_IS_UNICHAR && !LOG4CXX_LOGCHAR_IS_WCHAR
167
std::basic_ostringstream<logchar>& ThreadSpecificData::getStream(const logchar&)
168
0
{
169
0
  return getCurrentData()->m_priv->logchar_stringstream;
170
0
}
171
#endif
172
173
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
174
std::basic_ostringstream<wchar_t>& ThreadSpecificData::getStream(const wchar_t&)
175
0
{
176
0
  return getCurrentData()->m_priv->wchar_stringstream;
177
0
}
178
#endif
179
180
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
181
std::basic_ostringstream<UniChar>& ThreadSpecificData::getStream(const UniChar&)
182
{
183
  return getCurrentData()->m_priv->unichar_stringstream;
184
}
185
#endif
186
187
ThreadSpecificData* ThreadSpecificData::getCurrentData()
188
28.4k
{
189
28.4k
#if LOG4CXX_HAS_THREAD_LOCAL
190
28.4k
  thread_local ThreadSpecificData data;
191
28.4k
  return data.m_priv ? &data : NULL;
192
#elif APR_HAS_THREADS
193
  void* pData = NULL;
194
  if (APR_SUCCESS == apr_threadkey_private_get(&pData, APRInitializer::getTlsKey())
195
    && !pData)
196
  {
197
    pData = new ThreadSpecificData();
198
    if (APR_SUCCESS != apr_threadkey_private_set(pData, APRInitializer::getTlsKey()))
199
    {
200
      delete (ThreadSpecificData*)pData;
201
      pData = NULL;
202
    }
203
  }
204
  if (pData)
205
    return (ThreadSpecificData*) pData;
206
#endif
207
208
  // Fallback implementation that is not expected to be used
209
0
  using TaggedData = std::pair<std::thread::id, ThreadSpecificData>;
210
0
  static std::list<TaggedData> thread_id_map;
211
0
  static std::mutex mutex;
212
0
  std::lock_guard<std::mutex> lock(mutex);
213
0
  auto threadId = std::this_thread::get_id();
214
0
  auto pThreadId = std::find_if(thread_id_map.begin(), thread_id_map.end()
215
0
    , [threadId](const TaggedData& item) { return threadId == item.first; });
216
0
  if (thread_id_map.end() == pThreadId)
217
0
    pThreadId = thread_id_map.emplace(thread_id_map.begin(), threadId, ThreadSpecificData());
218
0
  return &pThreadId->second;
219
28.4k
}
220
221
void ThreadSpecificData::recycle()
222
10.0k
{
223
#if !LOG4CXX_HAS_THREAD_LOCAL && APR_HAS_THREADS
224
  if (m_priv->ndcStack.empty() && m_priv->mdcMap.empty())
225
  {
226
    void* pData = NULL;
227
    if (APR_SUCCESS == apr_threadkey_private_get(&pData, APRInitializer::getTlsKey())
228
      && pData == this
229
      && APR_SUCCESS == apr_threadkey_private_set(0, APRInitializer::getTlsKey()))
230
        delete this;
231
  }
232
#endif
233
10.0k
}
234
235
void ThreadSpecificData::put(const LogString& key, const LogString& val)
236
5.60k
{
237
5.60k
  if (auto p = getCurrentData())
238
5.60k
    p->getMap()[key] = val;
239
5.60k
}
240
241
void ThreadSpecificData::push(const LogString& val)
242
2.80k
{
243
2.80k
  auto p = getCurrentData();
244
2.80k
  if (!p)
245
0
    return;
246
2.80k
  NDC::Stack& stack = p->getStack();
247
2.80k
  if (stack.empty())
248
2.80k
  {
249
2.80k
    stack.push(NDC::DiagnosticContext(val, val));
250
2.80k
  }
251
0
  else
252
0
  {
253
0
    LogString fullMessage(stack.top().second);
254
0
    fullMessage.append(1, (logchar) 0x20);
255
0
    fullMessage.append(val);
256
0
    stack.push(NDC::DiagnosticContext(val, fullMessage));
257
0
  }
258
2.80k
}
259
260
void ThreadSpecificData::inherit(const NDC::Stack& src)
261
0
{
262
0
  if (auto p = getCurrentData())
263
0
    p->getStack() = src;
264
0
}
265