Skip to content

Commit dc404c7

Browse files
committed
Add logging out, add telemetry support
1 parent a8e3197 commit dc404c7

File tree

6 files changed

+175
-9
lines changed

6 files changed

+175
-9
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## 0.8.5
9+
* Enhancements
10+
* Add colored log output
11+
* Add telemetry support for execution
12+
713
## 0.8.4 (2022-03-09)
814
* Bugfix
915
* add missing excludes from dump function (collections)

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,49 @@ Failing operations return a `{:error, error}` tuple where `error` is a
274274
}}
275275
```
276276

277+
### Logging
278+
279+
You config the logging output by adding in your config file this line
280+
281+
```elixir
282+
config :mongodb_driver, log: true
283+
```
284+
285+
The attribute `log` supports `true`, `false` or a log level like `:info`. The default value is `false`. If you turn
286+
logging on, then you will see log output (command, collection, parameters):
287+
288+
```
289+
[info] CMD find "my-collection" [filter: [name: "Helga"]] db=2.1ms
290+
```
291+
292+
### Telemetry
293+
294+
The driver uses the [:telemetry](https://github.com/beam-telemetry/telemetry) package to emit the execution duration
295+
for each command. The event name is `[:mongodb_driver, :execution]` and the driver uses the following meta data:
296+
297+
```elixir
298+
metadata = %{
299+
type: :mongodb_driver,
300+
command: command,
301+
params: parameters,
302+
collection: collection,
303+
options: Keyword.get(opts, :telemetry_options, [])
304+
}
305+
306+
:telemetry.execute([:mongodb_driver, :execution], %{duration: duration}, metadata)
307+
```
308+
309+
In a Phoenix application with installed Phoenix Dashboard the metrics can be used by defining a metric in the Telemetry module:
310+
311+
```elixr
312+
summary("mongodb_driver.execution.duration",
313+
tags: [:collection, :command],
314+
unit: {:microsecond, :millisecond}
315+
),
316+
```
317+
318+
Then you see for each collection the execution time for each different command in the Dashboard metric page.
319+
277320
### Connection Pooling
278321

279322
The driver supports pooling by DBConnection (2.x). By default `mongodb_driver` will start a single

config/config.exs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ config :logger, :console,
1414
format: "$time [$level] $message ($metadata)\n\n",
1515
metadata: [:module, :function, :line]
1616

17+
config :mongodb_driver,
18+
log: true
19+
1720
# Sample configuration:
1821
#
1922
# config :logger, :console,

lib/mongo.ex

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,7 @@ defmodule Mongo do
799799
with {:ok, conn, new_cmd} <- Session.bind_session(session, cmd),
800800
{:ok, _cmd, response} <- DBConnection.execute(conn, %Query{action: :command}, [new_cmd], defaults(opts)),
801801
:ok <- Session.update_session(session, response, opts),
802-
{:ok, {_flags, doc}} <- check_for_error(response) do
802+
{:ok, {_flags, doc}} <- check_for_error(response, cmd, opts) do
803803
{:ok, doc}
804804
else
805805
{:error, error} ->
@@ -812,20 +812,20 @@ defmodule Mongo do
812812
{:ok, BSON.document() | nil} | {:error, Mongo.Error.t()}
813813
def exec_command(conn, cmd, opts) do
814814
with {:ok, _cmd, response} <- DBConnection.execute(conn, %Query{action: :command}, [cmd], defaults(opts)) do
815-
check_for_error(response)
815+
check_for_error(response, cmd, opts)
816816
end
817817
end
818818

819819
def exec_more_to_come(conn, opts) do
820820
with {:ok, _cmd, response} <- DBConnection.execute(conn, %Query{action: :command}, [:more_to_come], defaults(opts)) do
821-
check_for_error(response)
821+
check_for_error(response, [:more_to_come], opts)
822822
end
823823
end
824824

825825
##
826826
# Checks for an error and broadcast the event.
827827
##
828-
defp check_for_error({%{"ok" => ok} = response, %{request_id: request_id, operation_id: operation_id, connection_id: connection_id} = event, flags, duration}) when ok == 1 do
828+
defp check_for_error({%{"ok" => ok} = response, %{request_id: request_id, operation_id: operation_id, connection_id: connection_id} = event, flags, duration}, cmd, opts) when ok == 1 do
829829
Events.notify(
830830
%CommandSucceededEvent{
831831
reply: response,
@@ -838,10 +838,12 @@ defmodule Mongo do
838838
:commands
839839
)
840840

841+
do_log(cmd, duration, opts)
842+
841843
{:ok, {flags, response}}
842844
end
843845

844-
defp check_for_error({%{"ok" => ok} = response, event, flags, duration}) when ok == 1 do
846+
defp check_for_error({%{"ok" => ok} = response, event, flags, duration}, cmd, opts) when ok == 1 do
845847
Events.notify(
846848
%CommandSucceededEvent{
847849
reply: response,
@@ -851,10 +853,12 @@ defmodule Mongo do
851853
:commands
852854
)
853855

856+
do_log(cmd, duration, opts)
857+
854858
{:ok, {flags, response}}
855859
end
856860

857-
defp check_for_error({doc, %{request_id: request_id, operation_id: operation_id, connection_id: connection_id} = event, _flags, duration}) do
861+
defp check_for_error({doc, %{request_id: request_id, operation_id: operation_id, connection_id: connection_id} = event, _flags, duration}, cmd, opts) do
858862
error = Mongo.Error.exception(doc)
859863

860864
Events.notify(
@@ -869,10 +873,12 @@ defmodule Mongo do
869873
:commands
870874
)
871875

876+
do_log(cmd, duration, opts)
877+
872878
{:error, error}
873879
end
874880

875-
defp check_for_error({doc, event, _flags, duration}) do
881+
defp check_for_error({doc, event, _flags, duration}, cmd, opts) do
876882
error = Mongo.Error.exception(doc)
877883

878884
Events.notify(
@@ -884,6 +890,8 @@ defmodule Mongo do
884890
:commands
885891
)
886892

893+
do_log(cmd, duration, opts)
894+
887895
{:error, error}
888896
end
889897

@@ -1521,4 +1529,103 @@ defmodule Mongo do
15211529
defp defaults(opts) do
15221530
Keyword.put_new(opts, :timeout, @timeout)
15231531
end
1532+
1533+
## support for logging like Ecto
1534+
defp do_log([:more_to_come], _duration, _opts) do
1535+
:ok
1536+
end
1537+
1538+
defp do_log(cmd, duration, opts) do
1539+
case Keyword.has_key?(cmd, :isMaster) || Keyword.has_key?(cmd, :more_to_come) do
1540+
true ->
1541+
:ok
1542+
1543+
false ->
1544+
command =
1545+
cmd
1546+
|> Keyword.keys()
1547+
|> Enum.at(0)
1548+
1549+
{_, params} = Keyword.pop_first(cmd, command)
1550+
collection = Keyword.get(cmd, command)
1551+
do_log(command, collection, params, duration, opts)
1552+
end
1553+
end
1554+
1555+
defp do_log(command, collection, params, duration, opts) do
1556+
metadata = %{
1557+
type: :mongodb_driver,
1558+
command: command,
1559+
params: params,
1560+
collection: collection,
1561+
options: Keyword.get(opts, :telemetry_options, [])
1562+
}
1563+
1564+
:telemetry.execute([:mongodb_driver, :execution], %{duration: duration}, metadata)
1565+
1566+
log = Application.get_env(:mongodb_driver, :log, false)
1567+
1568+
case Keyword.get(opts, :log, log) do
1569+
true ->
1570+
Logger.log(:info, fn -> log_iodata(command, collection, params, duration) end, ansi_color: command_color(command))
1571+
1572+
false ->
1573+
:ok
1574+
1575+
level ->
1576+
Logger.log(level, fn -> log_iodata(command, collection, params, duration) end, ansi_color: command_color(command))
1577+
end
1578+
end
1579+
1580+
defp log_iodata(command, collection, params, duration) do
1581+
us = duration
1582+
ms = div(us, 100) / 10
1583+
1584+
[
1585+
"CMD",
1586+
?\s,
1587+
Atom.to_string(command),
1588+
?\s,
1589+
to_iodata(collection),
1590+
?\s,
1591+
to_iodata(params),
1592+
[?\s, "db", ?=, :io_lib_format.fwrite_g(ms), ?m, ?s]
1593+
]
1594+
end
1595+
1596+
defp to_iodata(doc) do
1597+
opts = Inspect.Opts.new([])
1598+
1599+
doc =
1600+
doc
1601+
|> Inspect.Algebra.to_doc(opts)
1602+
|> Inspect.Algebra.group()
1603+
1604+
Inspect.Algebra.format(doc, opts.width)
1605+
end
1606+
1607+
defp command_color(:getMore), do: :cyan
1608+
defp command_color(:ping), do: :cyan
1609+
defp command_color(:aggregate), do: :cyan
1610+
defp command_color(:find), do: :cyan
1611+
defp command_color(:count), do: :cyan
1612+
defp command_color(:distinct), do: :cyan
1613+
defp command_color(:listIndexes), do: :cyan
1614+
defp command_color(:listCollections), do: :cyan
1615+
defp command_color(:insert), do: :green
1616+
defp command_color(:delete), do: :red
1617+
defp command_color(:dropIndexes), do: :red
1618+
defp command_color(:drop), do: :red
1619+
defp command_color(:dropDatabase), do: :red
1620+
defp command_color(:dropUser), do: :red
1621+
defp command_color(:abortTransaction), do: :red
1622+
defp command_color(:update), do: :yellow
1623+
defp command_color(:findAndModify), do: :yellow
1624+
defp command_color(:createUser), do: :magenta
1625+
defp command_color(:createIndexes), do: :magenta
1626+
defp command_color(:create), do: :magenta
1627+
defp command_color(:killCursors), do: :magenta
1628+
defp command_color(:commitTransaction), do: :magenta
1629+
defp command_color(:configureFailPoint), do: :blue
1630+
defp command_color(_), do: nil
15241631
end

lib/mongo/topology_description.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,13 @@ defmodule Mongo.TopologyDescription do
6767
Updates the current measured round trip time for the specified server (address)
6868
"""
6969
def update_rrt(topology, address, round_trip_time) do
70-
put_in(topology.servers[address][:round_trip_time], round_trip_time)
70+
case topology.servers[address] do
71+
nil ->
72+
topology
73+
74+
_other ->
75+
put_in(topology.servers[address][:round_trip_time], round_trip_time)
76+
end
7177
end
7278

7379
@doc """

mix.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ defmodule Mongodb.Mixfile do
22
use Mix.Project
33

44
@source_url "https://github.com/zookzook/elixir-mongodb-driver"
5-
@version "0.8.4"
5+
@version "0.8.5"
66

77
def project() do
88
[
@@ -35,6 +35,7 @@ defmodule Mongodb.Mixfile do
3535

3636
defp deps do
3737
[
38+
{:telemetry, "~> 1.0"},
3839
{:db_connection, "~> 2.4.1"},
3940
{:decimal, "~> 2.0"},
4041
{:jason, "~> 1.3", only: [:dev, :test]},

0 commit comments

Comments
 (0)