jlog 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2c76ce9ecf9b6d897f60f7905da4f4a7f0d7fac3
4
+ data.tar.gz: 885cdc3455c73676a3e2c72af8edccb8fca615fc
5
+ SHA512:
6
+ metadata.gz: e4577db9d3d9bd21d4f0a841143820aadb56871729ae91180ff340127b0718f13afec51d52d0f94d55b3a6dc9dbf568080e6b8abbc0a4c4c9646e918fe50bd5a
7
+ data.tar.gz: 2912450e7590c18c5416f53c5abfba9935e9456955dc0db47b06313d9f2ad89ddbf6081ae69d2e0ff17b72858ee6f8e3d135048c917bb7bef7aa10a985a9237c
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'pry', require: false
7
+ gem 'rake-compiler', '~> 0.9'
8
+ gem 'minitest'
9
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ezekiel Templin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,64 @@
1
+ # JLog
2
+
3
+ Ruby C-bindings for OmniTI Labs' [Jlog](https://github.com/omniti-labs/jlog). Founded upon [work](https://github.com/mbainter/ruby-jlog/) by Mark Bainter (@mbainter.)
4
+
5
+ > JLog is short for "journaled log" and this package is really an API and implementation that is libjlog. What is libjlog? libjlog is a pure C, very simple durable message queue with multiple subscribers and publishers (both thread and multi-process safe). The basic concept is that publishers can open a log and write messages to it while subscribers open the log and consume messages from it. "That sounds easy." libjlog abstracts away the need to perform log rotation or maintenance by publishing into fixed size log buffers and eliminating old log buffers when there are no more consumers pending.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'jlog'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install jlog
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ log = Jlog.new('/var/log/logname')
25
+ log.add_subscriber 'LogSubscriber'
26
+ log.close
27
+
28
+ writer = Jlog::Writer.new('/var/log/logname')
29
+
30
+ writer.open
31
+ writer.write 'This is the first log message'
32
+ writer.write 'This is the second log message'
33
+ writer.write "This is the third log message, created at #{Time.now}"
34
+ writer.close
35
+
36
+ reader = Jlog::Reader.new '/var/log/logname'
37
+ reader.open 'LogSubscriber'
38
+
39
+ first = reader.read
40
+ second = reader.read
41
+ reader.rewind
42
+
43
+ if reader.read == second
44
+ puts "Rewind sets log position to last checkpoint."
45
+ end
46
+
47
+ reader.checkpoint
48
+
49
+ third = reader.read
50
+ reader.rewind
51
+ third_full = reader.read_message
52
+
53
+ if third == third_msg[:message]
54
+ ts = third_msg[:timestamp]
55
+ puts "#{third} and logged #{Time.at(ts)} (or #{ts} seconds since epoch)"
56
+ end
57
+
58
+ reader.checkpoint
59
+ reader.close
60
+ ```
61
+
62
+ ## Requirements
63
+
64
+ * [Jlog](https://github.com/omniti-labs/jlog)
@@ -0,0 +1,22 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/extensiontask"
3
+
4
+ spec = Gem::Specification.new("jlog.gemspec")
5
+
6
+ task :test => [:clobber, :compile] do
7
+ $LOAD_PATH.unshift('lib', 'spec')
8
+ Dir.glob('./spec/**/*_spec.rb') { |f| require f }
9
+ end
10
+
11
+ task :default => :test
12
+ task :spec => :test
13
+
14
+ Rake::ExtensionTask.new do |ext|
15
+ ext.name = "jlog"
16
+ ext.ext_dir = "ext/jlog"
17
+ ext.lib_dir = "lib/jlog"
18
+ ext.tmp_dir = "tmp"
19
+ ext.source_pattern = "*.c"
20
+ ext.config_options << '-Wall'
21
+ ext.gem_spec = spec
22
+ end
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ have_library('jlog')
4
+ have_header('jlog.h')
5
+ create_makefile('jlog/jlog')
@@ -0,0 +1,590 @@
1
+ #include <ruby.h>
2
+ #include <jlog.h>
3
+ #include <fcntl.h>
4
+ #include <stdio.h>
5
+ #include <sys/time.h>
6
+
7
+ typedef struct {
8
+ jlog_ctx *ctx;
9
+ char *path;
10
+ jlog_id start;
11
+ jlog_id last;
12
+ jlog_id prev;
13
+ jlog_id end;
14
+ int auto_checkpoint;
15
+ int error;
16
+ } jlog_obj;
17
+
18
+ typedef jlog_obj * Jlog;
19
+ typedef jlog_obj * Jlog_Writer;
20
+ typedef jlog_obj * Jlog_Reader;
21
+
22
+ VALUE cJlog;
23
+ VALUE cJlogWriter;
24
+ VALUE cJlogReader;
25
+ VALUE eJlog;
26
+
27
+ static VALUE message_sym;
28
+ static VALUE timestamp_sym;
29
+
30
+ void rJlog_populate_subscribers(VALUE);
31
+
32
+ void rJlog_mark(Jlog jo) { }
33
+
34
+ void rJlog_free(Jlog jo) {
35
+ if(jo->ctx) {
36
+ jlog_ctx_close(jo->ctx);
37
+ }
38
+
39
+ if(jo->path) {
40
+ xfree(jo->path);
41
+ }
42
+
43
+ if(jo) {
44
+ xfree(jo);
45
+ }
46
+ }
47
+
48
+ void rJlog_raise(Jlog jo, const char* mess)
49
+ {
50
+ VALUE e = rb_exc_new2(eJlog, mess);
51
+ rb_iv_set(e, "error", INT2FIX(jlog_ctx_err(jo->ctx)));
52
+ rb_iv_set(e, "errstr", rb_str_new2(jlog_ctx_err_string(jo->ctx)));
53
+ rb_iv_set(e, "errno", INT2FIX(jlog_ctx_errno(jo->ctx)));
54
+
55
+ rb_raise(eJlog, "%s: %d %s", mess, jlog_ctx_err(jo->ctx), jlog_ctx_err_string(jo->ctx));
56
+ }
57
+
58
+ VALUE rJlog_initialize(int argc, VALUE* argv, VALUE klass)
59
+ {
60
+ int options;
61
+ Jlog jo;
62
+ jlog_id zeroed = {0,0};
63
+ VALUE path;
64
+ VALUE optarg;
65
+ VALUE size;
66
+
67
+ rb_scan_args(argc, argv, "12", &path, &optarg, &size);
68
+
69
+ if(NIL_P(optarg)) {
70
+ options = O_CREAT;
71
+ } else {
72
+ options = (int)NUM2INT(optarg);
73
+ }
74
+
75
+ if(NIL_P(size)) {
76
+ size = (size_t)INT2FIX(0);
77
+ }
78
+
79
+ Data_Get_Struct(klass, jlog_obj, jo);
80
+
81
+ jo->ctx = jlog_new(StringValuePtr(path));
82
+ jo->path = strdup(StringValuePtr(path));
83
+ jo->auto_checkpoint = 0;
84
+ jo->start = zeroed;
85
+ jo->prev = zeroed;
86
+ jo->last = zeroed;
87
+ jo->end = zeroed;
88
+
89
+
90
+ if(!jo->ctx) {
91
+ rJlog_free(jo);
92
+ rb_raise(eJlog, "jlog_new(%s) failed", StringValuePtr(path));
93
+ }
94
+
95
+ if(options & O_CREAT) {
96
+ if(jlog_ctx_init(jo->ctx) != 0) {
97
+ if(jlog_ctx_err(jo->ctx) == JLOG_ERR_CREATE_EXISTS) {
98
+ if(options & O_EXCL) {
99
+ rJlog_free(jo);
100
+ rb_raise(eJlog, "file already exists: %s", StringValuePtr(path));
101
+ }
102
+ }else {
103
+ rJlog_raise(jo, "Error initializing jlog");
104
+ }
105
+ }
106
+ jlog_ctx_close(jo->ctx);
107
+ jo->ctx = jlog_new(StringValuePtr(path));
108
+ if(!jo->ctx) {
109
+ rJlog_free(jo);
110
+ rb_raise(eJlog, "jlog_new(%s) failed after successful init", StringValuePtr(path));
111
+ }
112
+ rJlog_populate_subscribers(klass);
113
+ }
114
+
115
+ if(!jo) {
116
+ rb_raise(eJlog, "jo wasn't initialized.");
117
+ }
118
+
119
+ return klass;
120
+ }
121
+
122
+ static VALUE rJlog_s_alloc(VALUE klass)
123
+ {
124
+ Jlog jo = ALLOC(jlog_obj);
125
+
126
+ return Data_Wrap_Struct(klass, rJlog_mark, rJlog_free, jo);
127
+ }
128
+
129
+ VALUE rJlog_add_subscriber(int argc, VALUE* argv, VALUE self)
130
+ {
131
+ VALUE s;
132
+ VALUE w;
133
+ unsigned int whence;
134
+ Jlog jo;
135
+
136
+ rb_scan_args(argc, argv, "11", &s, &w);
137
+
138
+ if(NIL_P(w)) {
139
+ whence = 0;
140
+ } else {
141
+ whence = NUM2UINT(w);
142
+ }
143
+
144
+ Data_Get_Struct(self, jlog_obj, jo);
145
+
146
+ if(!jo || !jo->ctx || jlog_ctx_add_subscriber(jo->ctx, StringValuePtr(s), whence) != 0) {
147
+ return Qfalse;
148
+ }
149
+
150
+ rJlog_populate_subscribers(self);
151
+
152
+ return Qtrue;
153
+ }
154
+
155
+
156
+ VALUE rJlog_remove_subscriber(VALUE self, VALUE subscriber)
157
+ {
158
+ Jlog jo;
159
+ int res;
160
+
161
+ Data_Get_Struct(self, jlog_obj, jo);
162
+ res = jlog_ctx_remove_subscriber(jo->ctx, StringValuePtr(subscriber));
163
+ if(!jo || !jo->ctx || res != 0)
164
+ {
165
+ return res;
166
+ }
167
+
168
+ rJlog_populate_subscribers(self);
169
+
170
+ return Qtrue;
171
+ }
172
+
173
+ void rJlog_populate_subscribers(VALUE self)
174
+ {
175
+ char **list;
176
+ int i;
177
+ Jlog jo;
178
+ VALUE subscribers = rb_ary_new();
179
+
180
+ Data_Get_Struct(self, jlog_obj, jo);
181
+
182
+ if(!jo || !jo->ctx)
183
+ {
184
+ rb_raise(eJlog, "Invalid jlog context");
185
+ }
186
+
187
+ jlog_ctx_list_subscribers(jo->ctx, &list);
188
+ for(i=0; list[i]; i++ ) {
189
+ rb_ary_push(subscribers, rb_str_new2(list[i]));
190
+ }
191
+ jlog_ctx_list_subscribers_dispose(jo->ctx, list);
192
+
193
+ rb_iv_set(self, "@subscribers", subscribers);
194
+ }
195
+
196
+ VALUE rJlog_list_subscribers(VALUE self)
197
+ {
198
+ Jlog jo;
199
+
200
+ Data_Get_Struct(self, jlog_obj, jo);
201
+
202
+ if(!jo || !jo->ctx)
203
+ {
204
+ rb_raise(eJlog, "Invalid jlog context");
205
+ }
206
+
207
+ rJlog_populate_subscribers(self);
208
+
209
+ return rb_iv_get(self, "@subscribers");
210
+ }
211
+
212
+ VALUE rJlog_raw_size(VALUE self)
213
+ {
214
+ size_t size;
215
+ Jlog jo;
216
+
217
+ Data_Get_Struct(self, jlog_obj, jo);
218
+
219
+ if(!jo || !jo->ctx) {
220
+ rb_raise(eJlog, "Invalid jlog context");
221
+ }
222
+ size = jlog_raw_size(jo->ctx);
223
+
224
+ return INT2NUM(size);
225
+ }
226
+
227
+ VALUE rJlog_close(VALUE self)
228
+ {
229
+ Jlog jo;
230
+
231
+ Data_Get_Struct(self, jlog_obj, jo);
232
+
233
+ if(!jo || !jo->ctx) return Qnil;
234
+
235
+ jlog_ctx_close(jo->ctx);
236
+ jo->ctx = NULL;
237
+
238
+ return Qtrue;
239
+ }
240
+
241
+ VALUE rJlog_inspect(VALUE self)
242
+ {
243
+ Jlog jo;
244
+
245
+ Data_Get_Struct(self, jlog_obj, jo);
246
+
247
+ if(!jo || !jo->ctx) return Qnil;
248
+
249
+ // XXX Fill in inspect call data
250
+
251
+ return Qtrue;
252
+ }
253
+
254
+ VALUE rJlog_destroy(VALUE self)
255
+ {
256
+ Jlog jo;
257
+
258
+ Data_Get_Struct(self, jlog_obj, jo);
259
+
260
+ if(!jo) return Qnil;
261
+
262
+ rJlog_free(jo);
263
+
264
+ return Qtrue;
265
+ }
266
+
267
+ VALUE rJlog_W_open(VALUE self)
268
+ {
269
+ Jlog_Writer jo;
270
+
271
+ Data_Get_Struct(self, jlog_obj, jo);
272
+
273
+ if(!jo || !jo->ctx) {
274
+ rb_raise(eJlog, "Invalid jlog context");
275
+ }
276
+
277
+ if(jlog_ctx_open_writer(jo->ctx) != 0) {
278
+ rJlog_raise(jo, "jlog_ctx_open_writer failed");
279
+ }
280
+
281
+ return Qtrue;
282
+ }
283
+
284
+ VALUE rJlog_W_write(VALUE self, VALUE msg)
285
+ {
286
+ int err;
287
+ Jlog_Writer jo;
288
+
289
+ Data_Get_Struct(self, jlog_obj, jo);
290
+
291
+ if(!jo || !jo->ctx) {
292
+ rb_raise(eJlog, "Invalid jlog context");
293
+ }
294
+
295
+ #if !defined(RSTRING_LEN)
296
+ # define RSTRING_LEN(x) (RSTRING(x)->len)
297
+ #endif
298
+ err = jlog_ctx_write(jo->ctx, StringValuePtr(msg), (size_t) RSTRING_LEN(msg));
299
+ if(err != 0) {
300
+ return Qfalse;
301
+ } else {
302
+ return Qtrue;
303
+ }
304
+ }
305
+
306
+
307
+ VALUE rJlog_R_open(VALUE self, VALUE subscriber)
308
+ {
309
+ Jlog_Reader jo;
310
+ int err;
311
+
312
+ Data_Get_Struct(self, jlog_obj, jo);
313
+
314
+ if(!jo || !jo->ctx) {
315
+ rb_raise(eJlog, "Invalid jlog context");
316
+ }
317
+
318
+ err = jlog_ctx_open_reader(jo->ctx, StringValuePtr(subscriber));
319
+
320
+ if(err != 0) {
321
+ rJlog_raise(jo, "jlog_ctx_open_reader failed");
322
+ }
323
+
324
+ return Qtrue;
325
+ }
326
+
327
+
328
+ VALUE rJlog_R_read(VALUE self)
329
+ {
330
+ const jlog_id epoch = {0, 0};
331
+ jlog_id cur = {0, 0};
332
+ jlog_message message;
333
+ int cnt;
334
+ Jlog_Reader jo;
335
+
336
+ Data_Get_Struct(self, jlog_obj, jo);
337
+
338
+ if(!jo || !jo->ctx) {
339
+ rb_raise(eJlog, "Invalid jlog context");
340
+ }
341
+
342
+ // If start is unset, read the interval
343
+ if(jo->error || !memcmp(&jo->start, &epoch, sizeof(jlog_id)))
344
+ {
345
+ jo->error = 0;
346
+ cnt = jlog_ctx_read_interval(jo->ctx, &jo->start, &jo->end);
347
+ if(cnt == 0 || (cnt == -1 && jlog_ctx_err(jo->ctx) == JLOG_ERR_FILE_OPEN)) {
348
+ jo->start = epoch;
349
+ jo->end = epoch;
350
+ return Qnil;
351
+ }
352
+ else if(cnt == -1)
353
+ rJlog_raise(jo, "jlog_ctx_read_interval_failed");
354
+ }
355
+
356
+ // If last is unset, start at the beginning
357
+ if(!memcmp(&jo->last, &epoch, sizeof(jlog_id))) {
358
+ cur = jo->start;
359
+ } else {
360
+ // if we've already read the end, return; otherwise advance
361
+ cur = jo->last;
362
+ if(!memcmp(&jo->prev, &jo->end, sizeof(jlog_id))) {
363
+ jo->start = epoch;
364
+ jo->end = epoch;
365
+ return Qnil;
366
+ }
367
+ jlog_ctx_advance_id(jo->ctx, &jo->last, &cur, &jo->end);
368
+ if(!memcmp(&jo->last, &cur, sizeof(jlog_id))) {
369
+ jo->start = epoch;
370
+ jo->end = epoch;
371
+ return Qnil;
372
+ }
373
+ }
374
+
375
+ if(jlog_ctx_read_message(jo->ctx, &cur, &message) != 0) {
376
+ if(jlog_ctx_err(jo->ctx) == JLOG_ERR_FILE_OPEN) {
377
+ jo->error = 1;
378
+ rJlog_raise(jo, "jlog_ctx_read_message failed");
379
+ return Qnil;
380
+ }
381
+
382
+ // read failed; raise error but recover if read is retried
383
+ jo->error = 1;
384
+ rJlog_raise(jo, "read failed");
385
+ }
386
+
387
+ if(jo->auto_checkpoint) {
388
+ if(jlog_ctx_read_checkpoint(jo->ctx, &cur) != 0)
389
+ rJlog_raise(jo, "checkpoint failed");
390
+
391
+ // must reread the interval after a checkpoint
392
+ jo->last = epoch;
393
+ jo->prev = epoch;
394
+ jo->start = epoch;
395
+ jo->end = epoch;
396
+ } else {
397
+ // update last
398
+ jo->prev = jo->last;
399
+ jo->last = cur;
400
+ }
401
+
402
+ return rb_str_new2(message.mess);
403
+ }
404
+
405
+ VALUE rJlog_R_read_message(VALUE self)
406
+ {
407
+ const jlog_id epoch = {0, 0};
408
+ jlog_id cur = {0, 0};
409
+ jlog_message message;
410
+ int cnt;
411
+ double ts;
412
+ Jlog_Reader jo;
413
+ VALUE message_hash;
414
+
415
+ Data_Get_Struct(self, jlog_obj, jo);
416
+
417
+ if(!jo || !jo->ctx) {
418
+ rb_raise(eJlog, "Invalid jlog context");
419
+ }
420
+
421
+ // If start is unset, read the interval
422
+ if(jo->error || !memcmp(&jo->start, &epoch, sizeof(jlog_id)))
423
+ {
424
+ jo->error = 0;
425
+ cnt = jlog_ctx_read_interval(jo->ctx, &jo->start, &jo->end);
426
+ if(cnt == 0 || (cnt == -1 && jlog_ctx_err(jo->ctx) == JLOG_ERR_FILE_OPEN)) {
427
+ jo->start = epoch;
428
+ jo->end = epoch;
429
+ return Qnil;
430
+ }
431
+ else if(cnt == -1)
432
+ rJlog_raise(jo, "jlog_ctx_read_interval_failed");
433
+ }
434
+
435
+ // If last is unset, start at the beginning
436
+ if(!memcmp(&jo->last, &epoch, sizeof(jlog_id))) {
437
+ cur = jo->start;
438
+ } else {
439
+ // if we've already read the end, return; otherwise advance
440
+ cur = jo->last;
441
+ if(!memcmp(&jo->prev, &jo->end, sizeof(jlog_id))) {
442
+ jo->start = epoch;
443
+ jo->end = epoch;
444
+ return Qnil;
445
+ }
446
+ jlog_ctx_advance_id(jo->ctx, &jo->last, &cur, &jo->end);
447
+ if(!memcmp(&jo->last, &cur, sizeof(jlog_id))) {
448
+ jo->start = epoch;
449
+ jo->end = epoch;
450
+ return Qnil;
451
+ }
452
+ }
453
+
454
+ if(jlog_ctx_read_message(jo->ctx, &cur, &message) != 0) {
455
+ if(jlog_ctx_err(jo->ctx) == JLOG_ERR_FILE_OPEN) {
456
+ jo->error = 1;
457
+ rJlog_raise(jo, "jlog_ctx_read_message failed");
458
+ return Qnil;
459
+ }
460
+
461
+ // read failed; raise error but recover if read is retried
462
+ jo->error = 1;
463
+ rJlog_raise(jo, "read failed");
464
+ }
465
+
466
+ if(jo->auto_checkpoint) {
467
+ if(jlog_ctx_read_checkpoint(jo->ctx, &cur) != 0)
468
+ rJlog_raise(jo, "checkpoint failed");
469
+
470
+ // must reread the interval after a checkpoint
471
+ jo->last = epoch;
472
+ jo->prev = epoch;
473
+ jo->start = epoch;
474
+ jo->end = epoch;
475
+ } else {
476
+ // update last
477
+ jo->prev = jo->last;
478
+ jo->last = cur;
479
+ }
480
+
481
+ ts = message.header->tv_sec+(message.header->tv_usec/1000000.0);
482
+
483
+ message_hash = rb_hash_new();
484
+ rb_hash_aset(message_hash, message_sym, rb_str_new2(message.mess));
485
+ rb_hash_aset(message_hash, timestamp_sym, rb_float_new(ts));
486
+
487
+
488
+ return message_hash;
489
+ }
490
+
491
+
492
+ VALUE rJlog_R_rewind(VALUE self)
493
+ {
494
+ Jlog_Reader jo;
495
+
496
+ Data_Get_Struct(self, jlog_obj, jo);
497
+
498
+ if(!jo || !jo->ctx) {
499
+ rb_raise(eJlog, "Invalid jlog context");
500
+ }
501
+
502
+ jo->last = jo->prev;
503
+
504
+ return Qtrue;
505
+ }
506
+
507
+
508
+ VALUE rJlog_R_checkpoint(VALUE self)
509
+ {
510
+ jlog_id epoch = { 0, 0 };
511
+ Jlog_Reader jo;
512
+
513
+ Data_Get_Struct(self, jlog_obj, jo);
514
+
515
+ if(!jo || !jo->ctx) {
516
+ rb_raise(eJlog, "Invalid jlog context");
517
+ }
518
+
519
+ if(memcmp(&jo->last, &epoch, sizeof(jlog_id)))
520
+ {
521
+ jlog_ctx_read_checkpoint(jo->ctx, &jo->last);
522
+
523
+ // re-read the interval
524
+ jo->last = epoch;
525
+ jo->start = epoch;
526
+ jo->end = epoch;
527
+ }
528
+
529
+ return Qtrue;
530
+ }
531
+
532
+
533
+ VALUE rJlog_R_auto_checkpoint(int argc, VALUE *argv, VALUE self)
534
+ {
535
+ Jlog jo;
536
+
537
+ Data_Get_Struct(self, jlog_obj, jo);
538
+
539
+ if(!jo || !jo->ctx) {
540
+ rb_raise(eJlog, "Invalid jlog context");
541
+ }
542
+
543
+ if(argc > 0) {
544
+ int ac = FIX2INT(argv[0]);
545
+ jo->auto_checkpoint = ac;
546
+ }
547
+
548
+ return INT2FIX(jo->auto_checkpoint);
549
+ }
550
+
551
+
552
+ void Init_jlog(void) {
553
+ message_sym = ID2SYM(rb_intern("message"));
554
+ timestamp_sym = ID2SYM(rb_intern("timestamp"));
555
+
556
+ cJlog = rb_define_class("Jlog", rb_cObject);
557
+ cJlogWriter = rb_define_class_under(cJlog, "Writer", cJlog);
558
+ cJlogReader = rb_define_class_under(cJlog, "Reader", cJlog);
559
+
560
+ eJlog = rb_define_class_under(cJlog, "Error", rb_eStandardError);
561
+
562
+ rb_define_method(cJlog, "initialize", rJlog_initialize, -1);
563
+ rb_define_alloc_func(cJlog, rJlog_s_alloc);
564
+
565
+ rb_define_method(cJlog, "add_subscriber", rJlog_add_subscriber, -1);
566
+ rb_define_method(cJlog, "remove_subscriber", rJlog_remove_subscriber, 1);
567
+ rb_define_method(cJlog, "list_subscribers", rJlog_list_subscribers, 0);
568
+ rb_define_method(cJlog, "raw_size", rJlog_raw_size, 0);
569
+ rb_define_method(cJlog, "close", rJlog_close, 0);
570
+ rb_define_method(cJlog, "destroy", rJlog_destroy, 0);
571
+ rb_define_method(cJlog, "inspect", rJlog_inspect, 0);
572
+
573
+ rb_define_alias(cJlog, "size", "raw_size");
574
+
575
+ rb_define_method(cJlogWriter, "initialize", rJlog_initialize, -1);
576
+ rb_define_alloc_func(cJlogWriter, rJlog_s_alloc);
577
+
578
+ rb_define_method(cJlogWriter, "open", rJlog_W_open, 0);
579
+ rb_define_method(cJlogWriter, "write", rJlog_W_write, 1);
580
+
581
+ rb_define_method(cJlogReader, "initialize", rJlog_initialize, -1);
582
+ rb_define_alloc_func(cJlogReader, rJlog_s_alloc);
583
+
584
+ rb_define_method(cJlogReader, "open", rJlog_R_open, 1);
585
+ rb_define_method(cJlogReader, "read", rJlog_R_read, 0);
586
+ rb_define_method(cJlogReader, "read_message", rJlog_R_read_message, 0);
587
+ rb_define_method(cJlogReader, "rewind", rJlog_R_rewind, 0);
588
+ rb_define_method(cJlogReader, "checkpoint", rJlog_R_checkpoint, 0);
589
+ rb_define_method(cJlogReader, "auto_checkpoint", rJlog_R_auto_checkpoint, -1);
590
+ }
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'jlog/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "jlog"
8
+ spec.version = Jlog::VERSION
9
+ spec.authors = ["Ezekiel Templin", "Tyler McMullen"]
10
+ spec.email = ["ezekiel@fastly.com", "tyler@fastly.com"]
11
+ spec.description = %q{Ruby C-extension for JLog}
12
+ spec.summary = %q{A Ruby C-extenion for using JLog}
13
+ spec.homepage = "https://github.com/fastly/jlog-ruby"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.extensions = ['ext/jlog/extconf.rb']
18
+ spec.test_files = ["spec"]
19
+ spec.require_paths = ["lib", "ext"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rake-compiler"
24
+ spec.add_development_dependency "minitest", "~> 5.0.8"
25
+ end
@@ -0,0 +1,5 @@
1
+ require "jlog/version"
2
+ require "jlog/jlog"
3
+
4
+ class Jlog
5
+ end
@@ -0,0 +1,3 @@
1
+ class Jlog
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Jlog::Reader" do
4
+ let(:jlog) { Jlog.new('/tmp/junit.log') }
5
+ let(:reader) { Jlog::Reader.new('/tmp/junit.log') }
6
+
7
+ before do
8
+ jlog.add_subscriber('TestSub')
9
+ writer = Jlog::Writer.new('/tmp/junit.log')
10
+
11
+ writer.open
12
+ 1.upto(10) do |n|
13
+ writer.write("Test Unit #{n}")
14
+ end
15
+ writer.close
16
+ end
17
+
18
+ it "should be able to access a reader subscribed to a log" do
19
+ reader.open('TestSub')
20
+ reader.close
21
+ end
22
+
23
+ it "should read a hash" do
24
+ reader.open('TestSub')
25
+ msg = reader.read_message
26
+ reader.checkpoint
27
+
28
+ assert_kind_of String, msg[:message]
29
+ assert_kind_of Float, msg[:timestamp]
30
+ end
31
+
32
+ it "should read from the proper checkpoint" do
33
+ reader.open('TestSub')
34
+ first_entry = reader.read
35
+ reader.close
36
+
37
+ reader = Jlog::Reader.new('/tmp/junit.log')
38
+ reader.open('TestSub')
39
+ assert_equal first_entry, reader.read, "Message wasn't appropriately checkpointed"
40
+ reader.close
41
+ end
42
+
43
+ it "should be able to rewind" do
44
+ reader.open('TestSub')
45
+ res1 = reader.read
46
+ reader.rewind
47
+ res2 = reader.read
48
+
49
+ assert_equal res1, res2, "Messages do not match"
50
+
51
+ reader.checkpoint
52
+ reader.close
53
+ end
54
+
55
+ it "should be able to checkpoint" do
56
+ reader.open('TestSub')
57
+ res1 = reader.read
58
+ reader.checkpoint
59
+ reader.rewind
60
+ res2 = reader.read
61
+
62
+ refute_equal res1, res2, "Checkpointed messages should not match"
63
+
64
+ reader.close
65
+ end
66
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Jlog::Writer' do
4
+ let(:writer) { Jlog::Writer.new('/tmp/junit.log') }
5
+
6
+ it "it should be able to open a log for writing" do
7
+ assert_kind_of Jlog::Writer, writer, "Jlog::Writer Object creation failed"
8
+ writer.open
9
+ writer.close
10
+ end
11
+
12
+ it "should be able to write to an open log" do
13
+ writer.open
14
+
15
+ 1.upto(10) do |n|
16
+ writer.write("Test Unit #{n}")
17
+ end
18
+
19
+ writer.close
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Jlog' do
4
+ let(:jlog) { Jlog.new('/tmp/junit.log') }
5
+
6
+ it "should work" do
7
+ assert_kind_of Jlog, jlog, "JLog Object creation failed"
8
+ jlog.close
9
+ end
10
+
11
+ it "should be able to add subscribers" do
12
+ jlog.add_subscriber("TestSub")
13
+ jlog.add_subscriber("TestSubRemove")
14
+
15
+ subscribers = jlog.list_subscribers
16
+ subscribers.delete_if do |sub|
17
+ sub =~ /^TestSub/
18
+ end
19
+
20
+ assert_equal 0, subscribers.size
21
+ jlog.close
22
+ end
23
+
24
+ it "should be able to remove subscribers" do
25
+ jlog.remove_subscriber("TestSubRemove")
26
+ jlog.close
27
+
28
+ jlog = Jlog.new('/tmp/junit.log')
29
+ jlog.list_subscribers.each do |s|
30
+ refute_equal "TestSubRemove", s, "Test Subscriber was not removed"
31
+ end
32
+ jlog.close
33
+ end
34
+
35
+ it "should be able to see subscribers added by others" do
36
+ assert_equal 0, jlog.list_subscribers.size
37
+ new_jlog = Jlog.new('/tmp/junit.log')
38
+ new_jlog.add_subscriber('NewSubscriber')
39
+ assert_equal 1, jlog.list_subscribers.size
40
+ assert_equal 1, new_jlog.list_subscribers.size
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+
4
+ #require File.expand_path('../../lib/jlog', __FILE__)
5
+ require 'jlog'
6
+
7
+ class Minitest::Spec
8
+ after do
9
+ if File.exists?('/tmp/junit.log')
10
+ system 'rm -rf /tmp/junit.log'
11
+ end
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jlog
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ezekiel Templin
8
+ - Tyler McMullen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.3'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.3'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake-compiler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: minitest
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 5.0.8
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 5.0.8
70
+ description: Ruby C-extension for JLog
71
+ email:
72
+ - ezekiel@fastly.com
73
+ - tyler@fastly.com
74
+ executables: []
75
+ extensions:
76
+ - ext/jlog/extconf.rb
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - ext/jlog/extconf.rb
85
+ - ext/jlog/jlog.c
86
+ - jlog.gemspec
87
+ - lib/jlog.rb
88
+ - lib/jlog/version.rb
89
+ - spec/jlog/reader_spec.rb
90
+ - spec/jlog/writer_spec.rb
91
+ - spec/jlog_spec.rb
92
+ - spec/spec_helper.rb
93
+ homepage: https://github.com/fastly/jlog-ruby
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ - ext
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.2.2
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: A Ruby C-extenion for using JLog
118
+ test_files: []