├── VERSION ├── config └── config.exs ├── test ├── test_helper.exs └── band_test.exs ├── .travis.yml ├── lib ├── band │ ├── stats.ex │ └── instrumenters │ │ ├── fuse.ex │ │ ├── plug.ex │ │ ├── ecto.ex │ │ ├── absinthe.ex │ │ └── phoenix.ex └── band.ex ├── .gitignore ├── README.md ├── mix.exs ├── mix.lock └── LICENSE /VERSION: -------------------------------------------------------------------------------- 1 | 0.5.2 2 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: elixir 2 | elixir: 3 | - '1.5' 4 | -------------------------------------------------------------------------------- /lib/band/stats.ex: -------------------------------------------------------------------------------- 1 | defmodule Band.Stats do 2 | def microseconds(time) do 3 | System.convert_time_unit(time, :native, :microsecond) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | -------------------------------------------------------------------------------- /lib/band/instrumenters/fuse.ex: -------------------------------------------------------------------------------- 1 | defmodule Band.Instrumenters.Fuse do 2 | @behaviour :fuse_stats_plugin 3 | 4 | @spec init(name::atom()) :: :ok 5 | def init(_name), do: :ok 6 | 7 | @spec increment(name :: atom(), counter :: :ok | :blown | :melt) :: :ok 8 | def increment(name, counter) do 9 | case counter do 10 | :ok -> 11 | Band.increment("fuse.ok", 1, tags: tags(name)) 12 | :blown -> 13 | Band.increment("fuse.blown", 1, tags: tags(name)) 14 | :melt -> 15 | Band.increment("fuse.melted", 1, tags: tags(name)) 16 | end 17 | end 18 | 19 | defp tags(name), do: ["name:#{name}"] 20 | end 21 | -------------------------------------------------------------------------------- /lib/band.ex: -------------------------------------------------------------------------------- 1 | defmodule Band do 2 | use Statix, runtime_config: true 3 | use GenServer 4 | 5 | def child_spec(config) do 6 | %{id: __MODULE__, 7 | start: {__MODULE__, :start_link, [config]}, 8 | restart: :temporary, 9 | type: :worker, 10 | shutdown: 5000} 11 | end 12 | 13 | def start_link(config) do 14 | GenServer.start_link(__MODULE__, config) 15 | end 16 | 17 | def init(config) do 18 | :band 19 | |> Application.get_all_env() 20 | |> Keyword.take([:prefix, :host, :port]) 21 | |> Keyword.merge(config) 22 | |> Enum.each(fn {key, value} -> Application.put_env(:statix, key, value) end) 23 | 24 | Band.connect() 25 | 26 | {:ok, nil} 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/band/instrumenters/plug.ex: -------------------------------------------------------------------------------- 1 | if Code.ensure_loaded?(Plug) do 2 | defmodule Band.Instrumenters.Plug do 3 | @moduledoc """ 4 | Instruments Plug connections to report each response to a client, 5 | with the request_method, request_path, and response_status as tags 6 | 7 | Usage: 8 | 9 | plug Band.Instrumenters.Plug 10 | 11 | """ 12 | 13 | alias Band.Stats 14 | alias Plug.Conn 15 | @behaviour Plug 16 | 17 | def init(_opts), do: nil 18 | 19 | 20 | def call(conn, _) do 21 | 22 | start = System.monotonic_time() 23 | 24 | Conn.register_before_send(conn, fn conn -> 25 | stop = System.monotonic_time() 26 | time_diff = Stats.microseconds(stop - start) 27 | 28 | tags = [request_method(conn), response_status(conn)] 29 | 30 | Band.histogram("plug.response", time_diff, tags: tags) 31 | 32 | conn 33 | end) 34 | end 35 | 36 | defp request_method(conn) do 37 | "request_method:#{conn.method}" 38 | end 39 | 40 | defp response_status(conn) do 41 | "response_status:#{Integer.to_string(conn.status)}" 42 | end 43 | 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /test/band_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BandTest do 2 | use ExUnit.Case 3 | doctest Band 4 | 5 | test "application env config" do 6 | Application.put_env(:band, :prefix, "default") 7 | Application.put_env(:band, :host, "localhost") 8 | Application.put_env(:band, :port, 8125) 9 | 10 | Band.start_link([]) 11 | 12 | statix = Application.get_all_env(:statix) 13 | assert statix[:prefix] == "default" 14 | assert statix[:host] == "localhost" 15 | assert statix[:port] == 8125 16 | end 17 | 18 | test "runtime config overrides application env" do 19 | Application.put_env(:band, :prefix, "default") 20 | Application.put_env(:band, :host, "statsd") 21 | Application.put_env(:band, :port, 8125) 22 | 23 | Band.start_link([prefix: "changed", host: "localhost", port: 1234]) 24 | 25 | statix = Application.get_all_env(:statix) 26 | 27 | assert statix[:prefix] == "changed" 28 | assert statix[:host] == "localhost" 29 | assert statix[:port] == 1234 30 | end 31 | 32 | test "child_spec/1" do 33 | assert Band.child_spec([]) == %{ 34 | id: Band, 35 | start: {Band, :start_link, [[]]}, 36 | restart: :temporary, 37 | type: :worker, 38 | shutdown: 5000 39 | } 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Band 2 | 3 | > A collection of instruments 4 | 5 | Band is a collection of instumentation helpers to aid in publishing metrics via 6 | `statsd`. 7 | 8 | ## Configuration 9 | 10 | Band can be configured statically or at runtime in order to be flexible for many 11 | different deployment strategies. 12 | 13 | If you wish to use `Mix` config, you can add the following to your `config.exs`: 14 | 15 | ```ex 16 | config :band, 17 | prefix: "my-app", 18 | host: "localhost", 19 | port: 8125 20 | ``` 21 | 22 | Then add the `Band` worker to your application's supervision tree: 23 | 24 | ```ex 25 | children = [ 26 | ... 27 | worker(Band, [[]]) 28 | ] 29 | ``` 30 | 31 | Band also supports `child_spec/1`: 32 | 33 | ```ex 34 | children = [ 35 | ... 36 | {Band, []} 37 | ] 38 | ``` 39 | 40 | If you need to configure Band at runtime, you can simply pass in the options 41 | directly into the child options: 42 | 43 | ```ex 44 | band_config = [ 45 | prefix: "my-app", 46 | host: System.get_env("BAND_HOST"), 47 | port: String.to_integer(System.get_env("BAND_PORT")) 48 | ] 49 | 50 | children = [ 51 | ... 52 | {Band, band_config} 53 | ] 54 | ``` 55 | 56 | Any options specified in the `Mix` config will be merged in as well. 57 | 58 | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) 59 | and published on [HexDocs](https://hexdocs.pm). Once published, the docs 60 | can be found at [https://hexdocs.pm/band](https://hexdocs.pm/band). 61 | -------------------------------------------------------------------------------- /lib/band/instrumenters/ecto.ex: -------------------------------------------------------------------------------- 1 | defmodule Band.Instrumenters.Ecto do 2 | alias Band.Stats 3 | 4 | def log(entry) do 5 | log_queue_time(entry) 6 | log_query_time(entry) 7 | log_decode_time(entry) 8 | log_total_time(entry) 9 | entry 10 | end 11 | 12 | def log_queue_time(entry) do 13 | metric = metric_name("queue", entry) 14 | time = value(entry.queue_time) 15 | Band.histogram(metric, time) 16 | end 17 | 18 | def log_query_time(entry) do 19 | metric = metric_name("query", entry) 20 | time = value(entry.query_time) 21 | Band.histogram(metric, time) 22 | end 23 | 24 | def log_decode_time(entry) do 25 | metric = metric_name("decode", entry) 26 | time = value(entry.decode_time) 27 | Band.histogram(metric, time) 28 | end 29 | 30 | def log_total_time(entry) do 31 | metric = metric_name("total", entry) 32 | time = total_time(entry) 33 | Band.histogram(metric, time) 34 | end 35 | 36 | defp total_time(entry) do 37 | total = 38 | zero_if_nil(entry.queue_time) + 39 | zero_if_nil(entry.query_time) + 40 | zero_if_nil(entry.decode_time) 41 | 42 | Stats.microseconds(total) 43 | end 44 | 45 | defp value(value) do 46 | value 47 | |> zero_if_nil 48 | |> Stats.microseconds 49 | end 50 | 51 | defp zero_if_nil(nil), do: 0 52 | defp zero_if_nil(value), do: value 53 | 54 | defp metric_name(metric, entry) do 55 | "ecto.query.duration.#{metric}.#{result(entry)}" 56 | end 57 | 58 | defp result(query) do 59 | case query.result do 60 | {:ok, _} -> 61 | "ok" 62 | {:error, _} -> 63 | "error" 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Band.Mixfile do 2 | use Mix.Project 3 | 4 | @version Path.join(__DIR__, "VERSION") 5 | |> File.read!() 6 | |> String.trim() 7 | 8 | def project do 9 | [ 10 | app: :band, 11 | version: @version, 12 | build_path: "_build", 13 | config_path: "config/config.exs", 14 | deps_path: "deps", 15 | lockfile: "mix.lock", 16 | elixir: "~> 1.5", 17 | start_permanent: Mix.env == :prod, 18 | deps: deps(), 19 | package: package(), 20 | description: description(), 21 | docs: doc(), 22 | ] 23 | end 24 | 25 | def application do 26 | [extra_applications: [:logger]] 27 | end 28 | 29 | defp deps do 30 | [ 31 | {:statix, "~> 1.1"}, 32 | {:absinthe, "~> 1.3", optional: true}, 33 | {:phoenix, "~> 1.3", optional: true}, 34 | {:fuse, "~> 2.4", optional: true}, 35 | {:plug, "~> 1.5", optional: true}, 36 | {:ex_doc, "~> 0.18", only: :dev}, 37 | ] 38 | end 39 | 40 | defp doc do 41 | [extras: ["README.md"], main: "readme"] 42 | end 43 | 44 | defp description do 45 | """ 46 | A collection of instrumenters for common elixir projects. 47 | """ 48 | end 49 | 50 | defp package do 51 | [ 52 | maintainers: ["Chris Keathley", "Greg Mefford", "Jeff Weiss", "Sonny Scroggin", "Jeff Gran"], 53 | files: package_files(), 54 | licenses: ["Apache 2.0"], 55 | links: %{"Github" => "https://github.com/letoteteam/band"} 56 | ] 57 | end 58 | 59 | defp package_files do 60 | [ 61 | "lib", 62 | "mix.exs", 63 | "README.md", 64 | "VERSION", 65 | "LICENSE", 66 | ] 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{"absinthe": {:hex, :absinthe, "1.4.10", "9f8d0c34dfcfd0030d3a3f123c7501e99ab59651731387289dad5885047ebb2a", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, 2 | "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"}, 3 | "ex_doc": {:hex, :ex_doc, "0.18.3", "f4b0e4a2ec6f333dccf761838a4b253d75e11f714b85ae271c9ae361367897b7", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, 4 | "fuse": {:hex, :fuse, "2.4.2", "9106b08db8793a34cc156177d7e24c41bd638ee1b28463cb76562fde213e8ced", [:rebar3], [], "hexpm"}, 5 | "mime": {:hex, :mime, "1.2.0", "78adaa84832b3680de06f88f0997e3ead3b451a440d183d688085be2d709b534", [:mix], [], "hexpm"}, 6 | "phoenix": {:hex, :phoenix, "1.3.2", "2a00d751f51670ea6bc3f2ba4e6eb27ecb8a2c71e7978d9cd3e5de5ccf7378bd", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [:mix], [], "hexpm"}, 8 | "plug": {:hex, :plug, "1.5.0", "224b25b4039bedc1eac149fb52ed456770b9678bbf0349cdd810460e1e09195b", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, 10 | "statix": {:hex, :statix, "1.1.0", "af9a4586c5cd58f879e0595f06a4790878715de5b8f75f6346693aaa38da2424", [:mix], [], "hexpm"}} 11 | -------------------------------------------------------------------------------- /lib/band/instrumenters/absinthe.ex: -------------------------------------------------------------------------------- 1 | if Code.ensure_loaded?(Absinthe) do 2 | defmodule Band.Instrumenters.Absinthe do 3 | alias Absinthe.Resolution 4 | alias Band.Stats 5 | 6 | @behaviour Absinthe.Middleware 7 | 8 | def instrument(middleware, %{__reference__: %{module: Absinthe.Type.BuiltIns.Introspection}}), do: middleware 9 | def instrument(middleware, _field), do: [__MODULE__ | middleware] 10 | 11 | def call(%Resolution{state: :unresolved}=res, _) do 12 | %{res | middleware: new_middleware(res)} 13 | end 14 | 15 | def after_resolve(%Resolution{state: :resolved}=res, [start_at: start_at, field: field, object: object]) do 16 | end_at = :erlang.monotonic_time() 17 | diff = end_at - start_at 18 | result = 19 | case res.errors do 20 | [] -> 21 | {:ok, res.value} 22 | errors -> 23 | {:error, errors} 24 | end 25 | 26 | observe(object, field, result, diff) 27 | 28 | res 29 | end 30 | 31 | defp observe(object, field, result, diff) do 32 | metric = metric_name(object, result) 33 | time = Stats.microseconds(diff) 34 | tags = [field(field), object(object)] 35 | Band.histogram(metric, time, tags: tags) 36 | end 37 | 38 | defp metric_name(:query, result) do 39 | "absinthe.query.#{result(result)}" 40 | end 41 | defp metric_name(_, result) do 42 | "absinthe.fields.#{result(result)}" 43 | end 44 | 45 | defp result({:ok, _}), do: "ok" 46 | defp result({:error, _}), do: "error" 47 | 48 | defp field(field), do: "field:#{field}" 49 | 50 | defp object(obj), do: "object:#{obj}" 51 | 52 | defp new_middleware(res) do 53 | res.middleware ++ after_middleware(res) 54 | end 55 | 56 | defp after_middleware(res), do: [ 57 | {{__MODULE__, :after_resolve}, 58 | start_at: :erlang.monotonic_time(), 59 | field: res.definition.schema_node.identifier, 60 | object: res.parent_type.identifier} 61 | ] 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/band/instrumenters/phoenix.ex: -------------------------------------------------------------------------------- 1 | defmodule Band.Instrumenters.Phoenix do 2 | alias Band.Stats 3 | 4 | def phoenix_controller_call(:start, compile, data) do 5 | Map.put(data, :compile, compile) 6 | end 7 | def phoenix_controller_call(:stop, time_diff, %{conn: conn}) do 8 | time = Stats.microseconds(time_diff) 9 | metric = "phoenix.controller.call_duration" 10 | tags = [controller(conn), action(conn)] 11 | Band.histogram(metric, time, tags: tags) 12 | end 13 | 14 | def phoenix_controller_render(:start, compile, data) do 15 | Map.put(data, :compile, compile) 16 | end 17 | def phoenix_controller_render(:stop, time_diff, %{view: view, template: template, conn: conn}) do 18 | time = Stats.microseconds(time_diff) 19 | metric = "phoenix.controller.render_duration" 20 | tags = [controller(conn), view(view), template(template)] 21 | Band.histogram(metric, time, tags: tags) 22 | end 23 | 24 | def phoenix_channel_join(:start, compile, data) do 25 | Map.put(data, :compile, compile) 26 | end 27 | def phoenix_channel_join(:stop, time_diff, %{socket: socket}) do 28 | time = Stats.microseconds(time_diff) 29 | metric = "phoenix.channel.join" 30 | tags = [channel(socket)] 31 | 32 | Band.histogram(metric, time, tags: tags) 33 | end 34 | 35 | def phoenix_channel_receive(:start, compile, data) do 36 | Map.put(data, :compile, compile) 37 | end 38 | def phoenix_channel_receive(:stop, time_diff, %{socket: socket, event: event}) do 39 | time = Stats.microseconds(time_diff) 40 | metric = "phoenix.channel.receive" 41 | tags = [channel(socket), handler(socket), event(event)] 42 | Band.histogram(metric, time, tags: tags) 43 | end 44 | 45 | defp controller(conn) do 46 | "controller:#{Phoenix.Controller.controller_module(conn)}" 47 | end 48 | 49 | def view(mod) do 50 | "view:#{mod}" 51 | end 52 | 53 | defp action(conn) do 54 | "action:#{Phoenix.Controller.action_name(conn)}" 55 | end 56 | 57 | defp template(template) do 58 | "template:#{template}" 59 | end 60 | 61 | defp channel(socket) do 62 | "channel:#{socket.channel}" 63 | end 64 | 65 | defp handler(socket) do 66 | "handler:#{socket.handler}" 67 | end 68 | 69 | defp event(event) do 70 | "event:#{event}" 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------