embulk-output-fluentd 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +27 -0
  3. data/.gitignore +80 -0
  4. data/.scalafmt.conf +2 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +59 -0
  7. data/build.gradle +83 -0
  8. data/build.sbt +32 -0
  9. data/gradle/wrapper/gradle-wrapper.jar +0 -0
  10. data/gradle/wrapper/gradle-wrapper.properties +6 -0
  11. data/gradlew +169 -0
  12. data/gradlew.bat +84 -0
  13. data/lib/embulk/output/fluentd.rb +3 -0
  14. data/project/build.properties +1 -0
  15. data/project/plugins.sbt +2 -0
  16. data/settings.gradle +1 -0
  17. data/src/main/resources/application.conf +8 -0
  18. data/src/main/scala/org/embulk/output/fluentd/ColumnVisitor.scala +43 -0
  19. data/src/main/scala/org/embulk/output/fluentd/FluentdOutputPlugin.scala +55 -0
  20. data/src/main/scala/org/embulk/output/fluentd/FluentdTransactionalPageOutput.scala +52 -0
  21. data/src/main/scala/org/embulk/output/fluentd/PluginTask.scala +36 -0
  22. data/src/main/scala/org/embulk/output/fluentd/sender/ActorManager.scala +29 -0
  23. data/src/main/scala/org/embulk/output/fluentd/sender/Sender.scala +131 -0
  24. data/src/main/scala/org/embulk/output/fluentd/sender/SenderBuilder.scala +40 -0
  25. data/src/main/scala/org/embulk/output/fluentd/sender/SenderFlow.scala +36 -0
  26. data/src/main/scala/org/embulk/output/fluentd/sender/SuperVisor.scala +46 -0
  27. data/src/test/scala/org/embulk/output/fluentd/TestActorManager.scala +42 -0
  28. data/src/test/scala/org/embulk/output/fluentd/sender/SenderFlowImplTest.scala +87 -0
  29. data/src/test/scala/org/embulk/output/fluentd/sender/SenderImplTest.scala +119 -0
  30. metadata +118 -0
@@ -0,0 +1,40 @@
1
+ package org.embulk.output.fluentd.sender
2
+
3
+ import java.time.Instant
4
+
5
+ import akka.actor.ActorSystem
6
+ import org.embulk.output.fluentd.PluginTask
7
+ import org.embulk.spi.Exec
8
+ import wvlet.airframe.{Design, newDesign}
9
+ import com.typesafe.config.ConfigFactory
10
+
11
+ object SenderBuilder {
12
+
13
+ def apply(task: PluginTask): Design = {
14
+ implicit val logger = Exec.getLogger(classOf[Sender])
15
+ implicit val system = ActorSystem("fluentd-sender",
16
+ ConfigFactory.load(this.getClass.getClassLoader, "application.conf"),
17
+ this.getClass.getClassLoader)
18
+ val timeKeyOpt = if (task.getTimeKey.isPresent) {
19
+ Some(task.getTimeKey.get())
20
+ } else None
21
+
22
+ newDesign
23
+ .bind[SenderFlow]
24
+ .toInstance(SenderFlowImpl(task.getTag, Instant.now().getEpochSecond, timeKeyOpt))
25
+ .bind[ActorManager]
26
+ .toInstance(ActorManagerImpl())
27
+ .bind[Sender]
28
+ .toProvider { (senderFlow: SenderFlow, actorManager: ActorManager) =>
29
+ SenderImpl(task.getHost,
30
+ task.getPort,
31
+ task.getRequestGroupingSize,
32
+ task.getAsyncSize,
33
+ senderFlow,
34
+ actorManager,
35
+ task.getRequestPerSeconds,
36
+ retryCount = 10)
37
+ }
38
+ }
39
+
40
+ }
@@ -0,0 +1,36 @@
1
+ package org.embulk.output.fluentd.sender
2
+
3
+ import akka.NotUsed
4
+ import akka.actor.ActorSystem
5
+ import akka.stream.scaladsl.{Flow, Tcp}
6
+ import akka.stream.scaladsl.Tcp.OutgoingConnection
7
+ import akka.util.ByteString
8
+ import org.velvia.MsgPack
9
+
10
+ import scala.concurrent.Future
11
+
12
+ trait SenderFlow {
13
+ val msgPackFlow: Flow[Seq[Seq[Map[String, AnyRef]]], (Int, ByteString), NotUsed]
14
+ def tcpConnectionFlow(host: String, port: Int)(
15
+ implicit s: ActorSystem): Flow[ByteString, ByteString, Future[OutgoingConnection]]
16
+ }
17
+
18
+ case class SenderFlowImpl private[sender] (tag: String, unixtime: Long, timeKeyOpt: Option[String])
19
+ extends SenderFlow {
20
+ override val msgPackFlow: Flow[Seq[Seq[Map[String, AnyRef]]], (Int, ByteString), NotUsed] =
21
+ Flow[Seq[Seq[Map[String, AnyRef]]]].map { value =>
22
+ val packing = value.flatten.map { v =>
23
+ val eventTime = for {
24
+ timeKey <- timeKeyOpt
25
+ timeValue <- v.get(timeKey)
26
+ } yield timeValue.toString.toLong
27
+ val logTime = eventTime.getOrElse(unixtime)
28
+ Seq(logTime, v)
29
+ }
30
+ (packing.size, ByteString(MsgPack.pack(Seq(tag, packing))))
31
+ }
32
+ override def tcpConnectionFlow(host: String, port: Int)(
33
+ implicit s: ActorSystem): Flow[ByteString, ByteString, Future[OutgoingConnection]] =
34
+ Tcp().outgoingConnection(host, port)
35
+
36
+ }
@@ -0,0 +1,46 @@
1
+ package org.embulk.output.fluentd.sender
2
+
3
+ import akka.actor._
4
+ import org.slf4j.Logger
5
+
6
+ class SuperVisor extends Actor {
7
+ var complete = 0
8
+ var failed = 0
9
+ var retried = 0
10
+ var counter = 0
11
+ var closed = false
12
+ override def receive: Receive = {
13
+ case Record(v) =>
14
+ counter = counter + v
15
+ case Complete(v) =>
16
+ complete = complete + v
17
+ case Failed(v) => failed = failed + v
18
+ case Retried(v) => retried = retried + v
19
+ case GetStatus =>
20
+ if (failed == 0) {
21
+ sender() ! Result(counter, complete, failed, retried)
22
+ } else {
23
+ sender() ! Stop(counter, complete, failed, retried)
24
+ }
25
+ case Close =>
26
+ val result = ClosedStatus(closed)
27
+ if (!closed) {
28
+ closed = true
29
+ }
30
+ sender() ! result
31
+ case LogStatus(logger) =>
32
+ logger.info(
33
+ s"$counter was queued and $complete records was completed. $failed records was failed and retried $retried records.")
34
+ }
35
+ }
36
+
37
+ case class Result(record: Int, complete: Int, failed: Int, retried: Int)
38
+ case object GetStatus
39
+ case class Stop(record: Int, complete: Int, failed: Int, retried: Int)
40
+ case object Close
41
+ case class ClosedStatus(alreadyClosed: Boolean)
42
+ case class LogStatus(logger: Logger)
43
+ case class Record(count: Int) extends AnyVal
44
+ case class Complete(count: Int) extends AnyVal
45
+ case class Failed(count: Int) extends AnyVal
46
+ case class Retried(count: Int) extends AnyVal
@@ -0,0 +1,42 @@
1
+ package org.embulk.output.fluentd
2
+
3
+ import java.io.IOException
4
+ import java.net.ServerSocket
5
+ import java.util.concurrent.ThreadLocalRandom
6
+
7
+ import akka.actor.{ActorRef, ActorSystem}
8
+ import akka.stream.ActorMaterializer
9
+ import akka.testkit.TestActorRef
10
+ import org.embulk.output.fluentd.sender._
11
+
12
+ import scala.concurrent.ExecutionContext
13
+
14
+ case class TestActorManager(s: ActorSystem) extends ActorManager {
15
+ implicit val system = s
16
+ val port: Int = freePort(8888, 8999)
17
+ val host: String = "127.0.0.1"
18
+
19
+ def freePort(from: Int, to: Int): Int = {
20
+ var port = from
21
+ while (true) {
22
+ if (isLocalPortFree(port)) return port
23
+ else port = ThreadLocalRandom.current.nextInt(from, to)
24
+ }
25
+ port
26
+ }
27
+
28
+ private def isLocalPortFree(port: Int) =
29
+ try {
30
+ new ServerSocket(port).close()
31
+ true
32
+ } catch {
33
+ case _: IOException =>
34
+ false
35
+ }
36
+
37
+ val testActorRef = TestActorRef(new SuperVisor)
38
+
39
+ override val supervisor: ActorRef = testActorRef
40
+ override implicit val materializer: ActorMaterializer = ActorMaterializer()
41
+ override implicit val dispatcher: ExecutionContext = ExecutionContext.global
42
+ }
@@ -0,0 +1,87 @@
1
+ package org.embulk.output.fluentd.sender
2
+
3
+ import akka.actor.ActorSystem
4
+ import akka.stream.{ActorMaterializer, ActorMaterializerSettings}
5
+ import akka.stream.scaladsl.Keep
6
+ import akka.stream.testkit.scaladsl.{TestSink, TestSource}
7
+ import akka.testkit.{ImplicitSender, TestKit}
8
+ import org.velvia.MsgPackUtils._
9
+ import org.embulk.output.fluentd.TestActorManager
10
+ import org.scalatest._
11
+
12
+ class SenderFlowImplTest
13
+ extends TestKit(ActorSystem("MySpec"))
14
+ with ImplicitSender
15
+ with FlatSpecLike
16
+ with Matchers
17
+ with BeforeAndAfterAll {
18
+
19
+ override def afterAll {
20
+ TestKit.shutdownActorSystem(system)
21
+ }
22
+
23
+ "msgPackFlow" should "converting Success" in {
24
+ val target = SenderFlowImpl("dummy", 123, None)
25
+ val actorManager = TestActorManager(system)
26
+ implicit val materializer = ActorMaterializer(ActorMaterializerSettings(system))
27
+ val map = Map[String, AnyRef]("a" -> Int.box(1), "b" -> "e")
28
+ val request = Seq(Seq(map), Seq(map))
29
+
30
+ val (pub, sub) = TestSource
31
+ .probe[Seq[Seq[Map[String, AnyRef]]]]
32
+ .via(target.msgPackFlow)
33
+ .toMat(TestSink.probe[(Int, akka.util.ByteString)])(Keep.both)
34
+ .run()
35
+
36
+ sub.request(n = 1)
37
+ pub.sendNext(request)
38
+
39
+ val (recordSize, result) = sub.expectNext()
40
+
41
+ val that = unpackSeq(result.toByteBuffer.array())
42
+ val records = that(1).asInstanceOf[Vector[_]]
43
+ recordSize should be(2) // record size
44
+ that.head should be("dummy")
45
+ records.size should be(2) // internal record size
46
+ val one = records(0).asInstanceOf[Vector[_]]
47
+ one(0) should be(123) // time
48
+ one(1).asInstanceOf[Map[_, _]] should be(map)
49
+
50
+ val two = records(1).asInstanceOf[Vector[_]]
51
+ two(0) should be(123) // time
52
+ two(1).asInstanceOf[Map[_, _]] should be(map)
53
+ }
54
+
55
+ "msgPackFlow" should "converting Success with time key" in {
56
+ val target = SenderFlowImpl("dummy", 123, Option("time"))
57
+ val actorManager = TestActorManager(system)
58
+ implicit val materializer = ActorMaterializer(ActorMaterializerSettings(system))
59
+ val map = Map[String, AnyRef]("a" -> Int.box(1), "b" -> "e", "time" -> Long.box(12345))
60
+ val request = Seq(Seq(map), Seq(map))
61
+
62
+ val (pub, sub) = TestSource
63
+ .probe[Seq[Seq[Map[String, AnyRef]]]]
64
+ .via(target.msgPackFlow)
65
+ .toMat(TestSink.probe[(Int, akka.util.ByteString)])(Keep.both)
66
+ .run()
67
+
68
+ sub.request(n = 1)
69
+ pub.sendNext(request)
70
+
71
+ val (recordSize, result) = sub.expectNext()
72
+
73
+ val that = unpackSeq(result.toByteBuffer.array())
74
+ val records = that(1).asInstanceOf[Vector[_]]
75
+ recordSize should be(2) // record size
76
+ that.head should be("dummy")
77
+ records.size should be(2) // internal record size
78
+ val one = records(0).asInstanceOf[Vector[_]]
79
+ one(0) should be(12345) // time
80
+ one(1).asInstanceOf[Map[_, _]] should be(map)
81
+
82
+ val two = records(1).asInstanceOf[Vector[_]]
83
+ two(0) should be(12345) // time
84
+ two(1).asInstanceOf[Map[_, _]] should be(map)
85
+ }
86
+
87
+ }
@@ -0,0 +1,119 @@
1
+ package org.embulk.output.fluentd.sender
2
+
3
+ import java.util.concurrent.TimeUnit
4
+
5
+ import akka.actor.ActorSystem
6
+ import akka.stream._
7
+ import akka.stream.scaladsl._
8
+ import akka.util.{ByteString, Timeout}
9
+ import org.scalatest.{BeforeAndAfterAll, FlatSpecLike, Matchers}
10
+ import org.embulk.output.fluentd.TestActorManager
11
+ import org.slf4j.helpers.NOPLogger
12
+
13
+ import scala.concurrent.duration._
14
+
15
+ class SenderImplTest extends FlatSpecLike with Matchers with BeforeAndAfterAll {
16
+
17
+ implicit val logger = NOPLogger.NOP_LOGGER
18
+ implicit val timeout = Timeout(5.seconds)
19
+
20
+ "Sending Success fast server" should "receive dummy server" in {
21
+
22
+ val system = ActorSystem("MySpec")
23
+ val actorManager = TestActorManager(system)
24
+ bootDummyServer(system, "127.0.0.1", actorManager.port, Duration.create(0, TimeUnit.SECONDS))
25
+ Thread.sleep(100) // wait for server boot.
26
+ val sender = SenderImpl(
27
+ "localhost",
28
+ port = actorManager.port,
29
+ groupedSize = 1,
30
+ asyncSize = 1,
31
+ SenderFlowImpl("tag", 0, None),
32
+ actorManager
33
+ )
34
+
35
+ (1 to 100).foreach { _ =>
36
+ sender(recode)
37
+ }
38
+
39
+ sender.waitForComplete()
40
+
41
+ actorManager.testActorRef.underlyingActor.counter should be(100)
42
+ actorManager.testActorRef.underlyingActor.complete should be(100)
43
+ actorManager.testActorRef.underlyingActor.retried should be(0)
44
+
45
+ actorManager.system.terminate()
46
+
47
+ }
48
+
49
+ "Sending Success slow server" should "receive dummy server" in {
50
+
51
+ val system = ActorSystem("MySpec")
52
+ val actorManager = TestActorManager(system)
53
+ bootDummyServer(system, "127.0.0.1", actorManager.port, Duration.create(3, TimeUnit.SECONDS))
54
+ Thread.sleep(100) // wait for server boot.
55
+ val sender = SenderImpl(
56
+ "localhost",
57
+ port = actorManager.port,
58
+ groupedSize = 1,
59
+ asyncSize = 1,
60
+ SenderFlowImpl("tag", 0, None),
61
+ actorManager
62
+ )
63
+
64
+ (1 to 2).foreach { _ =>
65
+ sender(recode)
66
+ }
67
+
68
+ sender.waitForComplete()
69
+
70
+ actorManager.testActorRef.underlyingActor.counter should be(2)
71
+ actorManager.testActorRef.underlyingActor.complete should be(2)
72
+ actorManager.testActorRef.underlyingActor.retried should be(0)
73
+
74
+ actorManager.system.terminate()
75
+ }
76
+
77
+ def recode: Seq[Map[String, AnyRef]] = Seq(Map[String, AnyRef]("a" -> Int.box(1), "b" -> "c"))
78
+
79
+ def bootDummyServer(system: ActorSystem, address: String, port: Int, duration: Duration): Unit = {
80
+ implicit val sys = system
81
+ implicit val materializer = ActorMaterializer()
82
+ val connections = Tcp().bind(address, port)
83
+ connections runForeach { connection =>
84
+ val echo = Flow[ByteString]
85
+ .via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true))
86
+ .map { v =>
87
+ TimeUnit.SECONDS.sleep(duration.toSeconds)
88
+ ByteString(v.utf8String)
89
+ }
90
+ connection.handleWith(echo)
91
+ }
92
+ }
93
+
94
+ "All Failure" should "retry count is correct" in {
95
+ val system = ActorSystem("MySpec")
96
+ val actorManager = TestActorManager(system)
97
+ val sender = SenderImpl(
98
+ "localhost",
99
+ port = 9999,
100
+ groupedSize = 1,
101
+ asyncSize = 1,
102
+ SenderFlowImpl("tag", 0, None),
103
+ actorManager,
104
+ retryCount = 2, // 2 times
105
+ retryDelayIntervalSecond = 1 // 1 seconds
106
+ )
107
+
108
+ val recode = Seq(Map[String, AnyRef]("a" -> Int.box(1), "b" -> "c"))
109
+ sender(recode)
110
+ sender.waitForComplete()
111
+
112
+ actorManager.testActorRef.underlyingActor.counter should be(1)
113
+ actorManager.testActorRef.underlyingActor.retried should be(2)
114
+ actorManager.testActorRef.underlyingActor.complete should be(0)
115
+
116
+ actorManager.system.terminate()
117
+ }
118
+
119
+ }
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: embulk-output-fluentd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - smdmts
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ~>
17
+ - !ruby/object:Gem::Version
18
+ version: '1.0'
19
+ name: bundler
20
+ prerelease: false
21
+ type: :development
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '10.0'
33
+ name: rake
34
+ prerelease: false
35
+ type: :development
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Dumps records to Fluentd.
42
+ email:
43
+ - smdmts@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .circleci/config.yml
49
+ - .gitignore
50
+ - .scalafmt.conf
51
+ - LICENSE.txt
52
+ - README.md
53
+ - build.gradle
54
+ - build.sbt
55
+ - gradle/wrapper/gradle-wrapper.jar
56
+ - gradle/wrapper/gradle-wrapper.properties
57
+ - gradlew
58
+ - gradlew.bat
59
+ - lib/embulk/output/fluentd.rb
60
+ - project/build.properties
61
+ - project/plugins.sbt
62
+ - settings.gradle
63
+ - src/main/resources/application.conf
64
+ - src/main/scala/org/embulk/output/fluentd/ColumnVisitor.scala
65
+ - src/main/scala/org/embulk/output/fluentd/FluentdOutputPlugin.scala
66
+ - src/main/scala/org/embulk/output/fluentd/FluentdTransactionalPageOutput.scala
67
+ - src/main/scala/org/embulk/output/fluentd/PluginTask.scala
68
+ - src/main/scala/org/embulk/output/fluentd/sender/ActorManager.scala
69
+ - src/main/scala/org/embulk/output/fluentd/sender/Sender.scala
70
+ - src/main/scala/org/embulk/output/fluentd/sender/SenderBuilder.scala
71
+ - src/main/scala/org/embulk/output/fluentd/sender/SenderFlow.scala
72
+ - src/main/scala/org/embulk/output/fluentd/sender/SuperVisor.scala
73
+ - src/test/scala/org/embulk/output/fluentd/TestActorManager.scala
74
+ - src/test/scala/org/embulk/output/fluentd/sender/SenderFlowImplTest.scala
75
+ - src/test/scala/org/embulk/output/fluentd/sender/SenderImplTest.scala
76
+ - classpath/airframe_2.11-0.15.jar
77
+ - classpath/akka-actor_2.11-2.5.3.jar
78
+ - classpath/akka-slf4j_2.11-2.5.3.jar
79
+ - classpath/akka-stream_2.11-2.5.3.jar
80
+ - classpath/config-1.3.1.jar
81
+ - classpath/embulk-core-0.8.25.jar
82
+ - classpath/embulk-output-fluentd-0.1.0.jar
83
+ - classpath/logback-core-1.1.7.jar
84
+ - classpath/msgpack4s_2.11-0.6.0.jar
85
+ - classpath/reactive-streams-1.0.0.jar
86
+ - classpath/scala-java8-compat_2.11-0.7.0.jar
87
+ - classpath/scala-library-2.11.11.jar
88
+ - classpath/scala-parser-combinators_2.11-1.0.4.jar
89
+ - classpath/scala-reflect-2.11.11.jar
90
+ - classpath/slf4j-api-1.7.23.jar
91
+ - classpath/ssl-config-core_2.11-0.2.1.jar
92
+ - classpath/surface_2.11-0.15.jar
93
+ - classpath/wvlet-log_2.11-1.2.3.jar
94
+ homepage: https://github.com/smdmts/embulk-output-fluentd
95
+ licenses:
96
+ - MIT
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
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.1.9
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Fluentd output plugin for Embulk
118
+ test_files: []