~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/logging_stats/logging_stats.cc

  • Committer: Daniel Nichter
  • Date: 2011-10-23 16:01:37 UTC
  • mto: This revision was merged to the branch mainline in revision 2448.
  • Revision ID: daniel@percona.com-20111023160137-7ac3blgz8z4tf8za
Add Administration Getting Started and Logging.  Capitalize SQL clause keywords.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2010 Joseph Daly <skinny.moey@gmail.com>
 
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
 *   * Redistributions of source code must retain the above copyright notice,
 
9
 *     this list of conditions and the following disclaimer.
 
10
 *   * 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
 *   * Neither the name of Joseph Daly nor the names of its contributors
 
14
 *     may be used to endorse or promote products derived from this software
 
15
 *     without specific prior written permission.
 
16
 *
 
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
18
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
20
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 
21
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
22
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
23
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
24
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
25
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
26
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 
27
 * THE POSSIBILITY OF SUCH DAMAGE.
 
28
 */
 
29
 
 
30
/**
 
31
 * @details
 
32
 *
 
33
 * This plugin tracks session and global statistics, as well as user statistics. 
 
34
 * The commands are logged using the post() and postEnd() logging APIs. 
 
35
 * The statistics are stored in a Scoreboard where each active session owns a 
 
36
 * ScoreboardSlot during the sessions active lifetime. 
 
37
 * 
 
38
 * Scoreboard
 
39
 *
 
40
 * The scoreboard is a pre-allocated vector of vectors of ScoreboardSlots. It 
 
41
 * can be thought of as a vector of buckets where each bucket contains 
 
42
 * pre-allocated ScoreboardSlots. To determine which bucket gets used for 
 
43
 * recording statistics the modulus operator is used on the session_id. This 
 
44
 * will result in a bucket to search for a unused ScoreboardSlot. Once a 
 
45
 * ScoreboardSlot is found the index of the slot is stored in the Session 
 
46
 * for later use. 
 
47
 *
 
48
 * Locking  
 
49
 * 
 
50
 * Each vector in the Scoreboard has its own lock. This allows session 2 
 
51
 * to not have to wait for session 1 to locate a slot to use, as they
 
52
 * will be in different buckets.  A lock is taken to locate a open slot
 
53
 * in the scoreboard. Subsequent queries by the session will not take
 
54
 * a lock.  
 
55
 *
 
56
 * A read lock is taken on the scoreboard vector when the table is queried 
 
57
 * in the data_dictionary. The "show status" and "show global status" do
 
58
 * not take a read lock when the data_dictionary table is queried, the 
 
59
 * user is not displayed in these results so it is not necessary. 
 
60
 *
 
61
 * Atomics
 
62
 *
 
63
 * The cumulative statistics use atomics, for the index into the vector
 
64
 * marking the last index that is used by a user. New users will increment
 
65
 * the atomic and claim the slot for use.  
 
66
 * 
 
67
 * System Variables
 
68
 * 
 
69
 * logging_stats_scoreboard_size - the size of the scoreboard this corresponds
 
70
 *   to the maximum number of concurrent connections that can be tracked
 
71
 *
 
72
 * logging_stats_max_user_count - this is used for cumulative statistics it 
 
73
 *   represents the maximum users that can be tracked 
 
74
 * 
 
75
 * logging_stats_bucket_count - the number of buckets to have in the scoreboard
 
76
 *   this splits up locking across several buckets so the entire scoreboard is 
 
77
 *   not locked at a single point in time.
 
78
 * 
 
79
 * logging_stats_enabled - enable/disable plugin 
 
80
 * 
 
81
 * TODO 
 
82
 *
 
83
 * Allow expansion of Scoreboard and cumulative vector 
 
84
 * 
 
85
 */
 
86
 
 
87
#include <config.h>
 
88
#include "user_commands.h"
 
89
#include "status_vars.h"
 
90
#include "global_stats.h"
 
91
#include "logging_stats.h"
 
92
#include "status_tool.h"
 
93
#include "stats_schema.h"
 
94
#include <boost/program_options.hpp>
 
95
#include <drizzled/module/option_map.h>
 
96
#include <drizzled/session.h>
 
97
#include <drizzled/session/times.h>
 
98
#include <drizzled/sql_lex.h>
 
99
#include <drizzled/statistics_variables.h>
 
100
 
 
101
namespace po= boost::program_options;
 
102
using namespace drizzled;
 
103
using namespace plugin;
 
104
using namespace std;
 
105
 
 
106
static bool sysvar_logging_stats_enabled= true;
 
107
 
 
108
typedef constrained_check<uint32_t, 50000, 10> scoreboard_size_constraint;
 
109
static scoreboard_size_constraint sysvar_logging_stats_scoreboard_size;
 
110
 
 
111
typedef constrained_check<uint32_t, 50000, 100> max_user_count_constraint;
 
112
static max_user_count_constraint sysvar_logging_stats_max_user_count;
 
113
 
 
114
typedef constrained_check<uint32_t, 500, 5> bucket_count_constraint;
 
115
static bucket_count_constraint sysvar_logging_stats_bucket_count;
 
116
 
 
117
LoggingStats::LoggingStats(string name_arg) : Logging(name_arg)
 
118
{
 
119
  current_scoreboard= new Scoreboard(sysvar_logging_stats_scoreboard_size, 
 
120
                                     sysvar_logging_stats_bucket_count);
 
121
 
 
122
  cumulative_stats= new CumulativeStats(sysvar_logging_stats_max_user_count); 
 
123
}
 
124
 
 
125
LoggingStats::~LoggingStats()
 
126
{
 
127
  delete current_scoreboard;
 
128
  delete cumulative_stats;
 
129
}
 
130
 
 
131
void LoggingStats::updateCurrentScoreboard(ScoreboardSlot *scoreboard_slot,
 
132
                                           Session *session)
 
133
{
 
134
  enum_sql_command sql_command= session->lex().sql_command;
 
135
 
 
136
  scoreboard_slot->getUserCommands()->logCommand(sql_command);
 
137
 
 
138
  /* If a flush occurred copy over values before setting new values */
 
139
  if (scoreboard_slot->getStatusVars()->hasBeenFlushed(session))
 
140
  {
 
141
    cumulative_stats->logGlobalStatusVars(scoreboard_slot);
 
142
  }
 
143
  scoreboard_slot->getStatusVars()->logStatusVar(session);
 
144
}
 
145
 
 
146
bool LoggingStats::resetGlobalScoreboard()
 
147
{
 
148
  cumulative_stats->getGlobalStatusVars()->reset();
 
149
  cumulative_stats->getGlobalStats()->getUserCommands()->reset();
 
150
 
 
151
  ScoreBoardVectors *vector_of_scoreboard_vectors=
 
152
    current_scoreboard->getVectorOfScoreboardVectors();
 
153
 
 
154
  ScoreBoardVectors::iterator v_of_scoreboard_v_begin_it= vector_of_scoreboard_vectors->begin();
 
155
 
 
156
  ScoreBoardVectors::iterator v_of_scoreboard_v_end_it= vector_of_scoreboard_vectors->end();
 
157
 
 
158
  for (; v_of_scoreboard_v_begin_it != v_of_scoreboard_v_end_it; ++v_of_scoreboard_v_begin_it)
 
159
  {
 
160
    std::vector<ScoreboardSlot* > *scoreboard_vector= *v_of_scoreboard_v_begin_it;
 
161
 
 
162
    std::vector<ScoreboardSlot* >::iterator scoreboard_vector_it= scoreboard_vector->begin();
 
163
    std::vector<ScoreboardSlot* >::iterator scoreboard_vector_end= scoreboard_vector->end();
 
164
    for (; scoreboard_vector_it != scoreboard_vector_end; ++scoreboard_vector_it)
 
165
    {
 
166
      ScoreboardSlot *scoreboard_slot= *scoreboard_vector_it;
 
167
      scoreboard_slot->getStatusVars()->reset();
 
168
      scoreboard_slot->getUserCommands()->reset();
 
169
    }
 
170
  }
 
171
 
 
172
  return false;
 
173
}
 
174
 
 
175
bool LoggingStats::post(Session *session)
 
176
{
 
177
  if (! isEnabled() || (session->getSessionId() == 0))
 
178
  {
 
179
    return false;
 
180
  }
 
181
 
 
182
  ScoreboardSlot *scoreboard_slot= current_scoreboard->findScoreboardSlotToLog(session);
 
183
 
 
184
  /* Its possible that the scoreboard is full with active sessions in which case 
 
185
     this could be null */
 
186
  if (scoreboard_slot)
 
187
  {
 
188
    updateCurrentScoreboard(scoreboard_slot, session);
 
189
  }
 
190
  return false;
 
191
}
 
192
 
 
193
bool LoggingStats::postEnd(Session *session)
 
194
{
 
195
  if (! isEnabled() || (session->getSessionId() == 0))
 
196
  {
 
197
    return false;
 
198
  }
 
199
 
 
200
  bool isInScoreboard= false;
 
201
  ScoreboardSlot *scoreboard_slot= current_scoreboard->findOurScoreboardSlot(session);
 
202
 
 
203
  if (scoreboard_slot)
 
204
  {
 
205
    isInScoreboard= true;
 
206
  } 
 
207
  else 
 
208
  { 
 
209
    /* the session did not have a slot reserved, that could be because the scoreboard was
 
210
       full, but most likely its a failed authentication so post() is never called where
 
211
       the slot is assigned. Log the global status values below, and if the user has a slot
 
212
       log to it, but do not reserve a new slot for a user. If it was a failed authentication
 
213
       the scoreboard would be filled up quickly with invalid users. 
 
214
    */
 
215
    scoreboard_slot= new ScoreboardSlot();
 
216
    scoreboard_slot->setUser(session->user()->username());
 
217
    scoreboard_slot->setIp(session->user()->address());
 
218
  }
 
219
 
 
220
  scoreboard_slot->getStatusVars()->logStatusVar(session);
 
221
  boost::posix_time::ptime end(boost::posix_time::microsec_clock::universal_time());
 
222
  uint64_t end_time= (end - session->times.epoch()).total_seconds();
 
223
  scoreboard_slot->getStatusVars()->getStatusVarCounters()->connection_time= end_time - session->times.getConnectSeconds();
 
224
 
 
225
  cumulative_stats->logUserStats(scoreboard_slot, isInScoreboard);
 
226
  cumulative_stats->logGlobalStats(scoreboard_slot);
 
227
  cumulative_stats->logGlobalStatusVars(scoreboard_slot);
 
228
 
 
229
  if (isInScoreboard)
 
230
  {
 
231
    scoreboard_slot->reset();
 
232
  } 
 
233
  else 
 
234
  {
 
235
    delete scoreboard_slot;
 
236
  } 
 
237
 
 
238
  return false;
 
239
}
 
240
 
 
241
/* Plugin initialization and system variables */
 
242
 
 
243
static LoggingStats *logging_stats= NULL;
 
244
 
 
245
static CurrentCommandsTool *current_commands_tool= NULL;
 
246
 
 
247
static CumulativeCommandsTool *cumulative_commands_tool= NULL;
 
248
 
 
249
static GlobalStatementsTool *global_statements_tool= NULL;
 
250
 
 
251
static SessionStatementsTool *session_statements_tool= NULL;
 
252
 
 
253
static StatusTool *global_status_tool= NULL;
 
254
 
 
255
static StatusTool *session_status_tool= NULL;
 
256
 
 
257
static CumulativeUserStatsTool *cumulative_user_stats_tool= NULL;
 
258
 
 
259
static ScoreboardStatsTool *scoreboard_stats_tool= NULL;
 
260
 
 
261
static void enable(Session *, sql_var_t)
 
262
{
 
263
  if (logging_stats)
 
264
  {
 
265
    if (sysvar_logging_stats_enabled)
 
266
    {
 
267
      logging_stats->enable();
 
268
    }
 
269
    else
 
270
    {
 
271
      logging_stats->disable();
 
272
    }
 
273
  }
 
274
}
 
275
 
 
276
static int init(drizzled::module::Context &context)
 
277
{
 
278
  const module::option_map &vm= context.getOptions();
 
279
  sysvar_logging_stats_enabled= not vm.count("disable");
 
280
 
 
281
  logging_stats= new LoggingStats("logging_stats");
 
282
  current_commands_tool= new CurrentCommandsTool(logging_stats);
 
283
  cumulative_commands_tool= new CumulativeCommandsTool(logging_stats);
 
284
  global_statements_tool= new GlobalStatementsTool(logging_stats);
 
285
  session_statements_tool= new SessionStatementsTool(logging_stats);
 
286
  session_status_tool= new StatusTool(logging_stats, true);
 
287
  global_status_tool= new StatusTool(logging_stats, false);
 
288
  cumulative_user_stats_tool= new CumulativeUserStatsTool(logging_stats);
 
289
  scoreboard_stats_tool= new ScoreboardStatsTool(logging_stats);
 
290
 
 
291
  context.add(logging_stats);
 
292
  context.add(current_commands_tool);
 
293
  context.add(cumulative_commands_tool);
 
294
  context.add(global_statements_tool);
 
295
  context.add(session_statements_tool);
 
296
  context.add(session_status_tool);
 
297
  context.add(global_status_tool);
 
298
  context.add(cumulative_user_stats_tool);
 
299
  context.add(scoreboard_stats_tool);
 
300
 
 
301
  if (sysvar_logging_stats_enabled)
 
302
    logging_stats->enable();
 
303
 
 
304
  context.registerVariable(new sys_var_constrained_value_readonly<uint32_t>("max_user_count", sysvar_logging_stats_max_user_count));
 
305
  context.registerVariable(new sys_var_constrained_value_readonly<uint32_t>("bucket_count", sysvar_logging_stats_bucket_count));
 
306
  context.registerVariable(new sys_var_constrained_value_readonly<uint32_t>("scoreboard_size", sysvar_logging_stats_scoreboard_size));
 
307
  context.registerVariable(new sys_var_bool_ptr("enable", &sysvar_logging_stats_enabled, enable));
 
308
 
 
309
  return 0;
 
310
}
 
311
 
 
312
 
 
313
static void init_options(drizzled::module::option_context &context)
 
314
{
 
315
  context("max-user-count",
 
316
          po::value<max_user_count_constraint>(&sysvar_logging_stats_max_user_count)->default_value(500),
 
317
          _("Max number of users that will be logged"));
 
318
  context("bucket-count",
 
319
          po::value<bucket_count_constraint>(&sysvar_logging_stats_bucket_count)->default_value(10),
 
320
          _("Max number of range locks to use for Scoreboard"));
 
321
  context("scoreboard-size",
 
322
          po::value<scoreboard_size_constraint>(&sysvar_logging_stats_scoreboard_size)->default_value(2000),
 
323
          _("Max number of concurrent sessions that will be logged"));
 
324
  context("disable", _("Enable Logging Statistics Collection"));
 
325
}
 
326
 
 
327
DRIZZLE_DECLARE_PLUGIN
 
328
{
 
329
  DRIZZLE_VERSION_ID,
 
330
  "logging_stats",
 
331
  "0.1",
 
332
  "Joseph Daly",
 
333
  N_("User Statistics as DATA_DICTIONARY tables"),
 
334
  PLUGIN_LICENSE_BSD,
 
335
  init,   /* Plugin Init      */
 
336
  NULL, /* depends */
 
337
  init_options    /* config options   */
 
338
}
 
339
DRIZZLE_DECLARE_PLUGIN_END;