From 3927c7c98fcc882e65235988d3f2e0e97596316e Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 08:24:59 +0100 Subject: [PATCH 001/405] basic cowboy app, and a butchered version of rb --- .gitignore | 4 + Makefile | 30 + README | 5 + apps/bigwig/src/bigwig.app.src | 12 + apps/bigwig/src/bigwig.erl | 19 + apps/bigwig/src/bigwig_app.erl | 16 + apps/bigwig/src/bigwig_http.erl | 58 ++ apps/bigwig/src/bigwig_http_vm.erl | 87 +++ apps/bigwig/src/bigwig_sup.erl | 31 ++ apps/bigwig/src/mochijson2.erl | 849 +++++++++++++++++++++++++++++ apps/bigwig/src/myserver.erl | 49 ++ apps/bigwig/src/rb2.erl | 504 +++++++++++++++++ apps/bigwig/src/rb_format_supp.erl | 155 ++++++ apps/bigwig/src/reloader.erl | 161 ++++++ apps/bigwig/src/user_default.erl | 138 +++++ rebar | Bin 0 -> 102487 bytes start-dev.sh | 4 + sys.config | 10 + 18 files changed, 2132 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README create mode 100644 apps/bigwig/src/bigwig.app.src create mode 100644 apps/bigwig/src/bigwig.erl create mode 100644 apps/bigwig/src/bigwig_app.erl create mode 100644 apps/bigwig/src/bigwig_http.erl create mode 100644 apps/bigwig/src/bigwig_http_vm.erl create mode 100644 apps/bigwig/src/bigwig_sup.erl create mode 100644 apps/bigwig/src/mochijson2.erl create mode 100644 apps/bigwig/src/myserver.erl create mode 100644 apps/bigwig/src/rb2.erl create mode 100644 apps/bigwig/src/rb_format_supp.erl create mode 100644 apps/bigwig/src/reloader.erl create mode 100644 apps/bigwig/src/user_default.erl create mode 100755 rebar create mode 100755 start-dev.sh create mode 100644 sys.config diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..86848fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.* +*~ +*.beam +*.dump diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..37f1248 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +# See LICENSE for licensing information. + +DIALYZER = dialyzer +REBAR = ./rebar + +all: app + +app: + @$(REBAR) compile + +clean: + @$(REBAR) clean + rm -f erl_crash.dump + +tests: clean app eunit ct + +eunit: + @$(REBAR) eunit + +ct: + @$(REBAR) ct + +build-plt: + @$(DIALYZER) --build_plt --output_plt .bigwig_dialyzer.plt \ + --apps kernel stdlib sasl inets crypto public_key ssl + +dialyze: + @$(DIALYZER) --src src --plt .bigwig_dialyzer.plt \ + -Wbehaviours -Werror_handling \ + -Wrace_conditions -Wunmatched_returns # -Wunderspecs diff --git a/README b/README new file mode 100644 index 0000000..b77b1fb --- /dev/null +++ b/README @@ -0,0 +1,5 @@ +bigwig is a web interface to a running erlang system. + +you get stats on the state of the VM, releases, apps etc + +Also, a web-based report-browser, etop, appmon etc etc diff --git a/apps/bigwig/src/bigwig.app.src b/apps/bigwig/src/bigwig.app.src new file mode 100644 index 0000000..abbaa05 --- /dev/null +++ b/apps/bigwig/src/bigwig.app.src @@ -0,0 +1,12 @@ +{application, bigwig, + [ + {description, "web-based suite of tools for interrogating and erlang system"}, + {vsn, "0.1"}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {mod, { bigwig_app, []}}, + {env, []} + ]}. diff --git a/apps/bigwig/src/bigwig.erl b/apps/bigwig/src/bigwig.erl new file mode 100644 index 0000000..7280c79 --- /dev/null +++ b/apps/bigwig/src/bigwig.erl @@ -0,0 +1,19 @@ +-module(bigwig). +-export([start/0, stop/0]). + +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. + +start() -> + ensure_started(crypto), + ensure_started(sasl), + ensure_started(cowboy), + application:start(bigwig). + +stop() -> + application:stop(bigwig). diff --git a/apps/bigwig/src/bigwig_app.erl b/apps/bigwig/src/bigwig_app.erl new file mode 100644 index 0000000..94492f4 --- /dev/null +++ b/apps/bigwig/src/bigwig_app.erl @@ -0,0 +1,16 @@ +-module(bigwig_app). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +%% =================================================================== +%% Application callbacks +%% =================================================================== + +start(_StartType, _StartArgs) -> + bigwig_sup:start_link(). + +stop(_State) -> + ok. diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl new file mode 100644 index 0000000..c218767 --- /dev/null +++ b/apps/bigwig/src/bigwig_http.erl @@ -0,0 +1,58 @@ +-module(bigwig_http). +-behaviour(gen_server). +-define(SERVER, ?MODULE). + +-export([start_link/0]). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +-record(state, {}). + +%% ------------------------------------------------------------------ +%% API Function Definitions +%% ------------------------------------------------------------------ + +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +%% ------------------------------------------------------------------ +%% gen_server Function Definitions +%% ------------------------------------------------------------------ + +init([]) -> + + Dispatch = [ + %% {Host, list({Path, Handler, Opts})} + {'_', [{'_', bigwig_http_vm, []}]} + ], + + Port = 8080, + error_logger:info_msg("Starting http server on port ~p", [Port]), + + %% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts + cowboy:start_listener(http, 100, + cowboy_tcp_transport, [{port, Port}], + cowboy_http_protocol, [{dispatch, Dispatch}] + ), + + {ok, #state{}}. + +handle_call(_Request, _From, State) -> + {noreply, ok, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%% ------------------------------------------------------------------ +%% Internal Function Definitions +%% ------------------------------------------------------------------ + diff --git a/apps/bigwig/src/bigwig_http_vm.erl b/apps/bigwig/src/bigwig_http_vm.erl new file mode 100644 index 0000000..07f2903 --- /dev/null +++ b/apps/bigwig/src/bigwig_http_vm.erl @@ -0,0 +1,87 @@ +%% +%% show details on the VM, releases, apps, etc. +%% +-module(bigwig_http_vm). +-behaviour(cowboy_http_handler). +-export([init/3, handle/2, terminate/2]). + +-compile(export_all). + +init({tcp, http}, Req, _Opts) -> + {ok, Req, undefined_state}. + +handle(Req, State) -> + Body = mochijson2:encode(all()), + Headers = [{<<"Content-Type">>, <<"application/json">>}], + {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. + +terminate(_Req, _State) -> + ok. + + +all() -> + {struct, [ + {system_info, {struct, system_info()}}, + {releases, {struct, releases()}}, + {applications,{struct, applications()}} + ]}. + + +%% Funs to load data about aspects of the system, mochiJSON formatted: + +system_info() -> + Fmt = fun(V) when is_number(V) -> V ; + (V) when is_list(V) -> list_to_binary(V) ; + (V) when is_atom(V) -> V end, + + Keys = [ + process_count, + process_limit, + kernel_poll, + logical_processors, + otp_release, + system_architecture + ], + + [ {N, Fmt(erlang:system_info(N))} || N <- Keys ]. + + +releases() -> + Rels = release_handler:which_releases(), + %% TODO parse out the name/version of the apps? + FmtDeps = fun(L) -> [ list_to_binary(A) || A <- L ] end, + [ + {list_to_binary(Name), {struct, + [ + {version, list_to_binary(Version)}, + {status, list_to_binary(atom_to_list(Status))}, + {deps, FmtDeps(Deps)} + ]}} + || {Name, Version, Deps, Status} <- Rels + ]. + + +applications() -> + %% Treat loaded as (Loaded - Running) + Which = application:which_applications(), + Loaded0 = application:loaded_applications(), + Loaded = lists:filter(fun({Name, _Desc, _Ver}) -> + lists:keyfind(Name, 1, Which) =:= false + end, Loaded0), + + Format = fun(AppList) -> + {struct, + [ {Name, + {struct, [ {description, list_to_binary(Desc)}, + {version, list_to_binary(Ver)} ]} } + || {Name, Desc, Ver} <- AppList + ]} + end, + + [ + {running, Format(Which)}, + {loaded, Format(Loaded)} + ]. + + diff --git a/apps/bigwig/src/bigwig_sup.erl b/apps/bigwig/src/bigwig_sup.erl new file mode 100644 index 0000000..5fb7407 --- /dev/null +++ b/apps/bigwig/src/bigwig_sup.erl @@ -0,0 +1,31 @@ +-module(bigwig_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +%% Helper macro for declaring children of supervisor +-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). + +%% =================================================================== +%% API functions +%% =================================================================== + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% =================================================================== +%% Supervisor callbacks +%% =================================================================== + +init([]) -> + Http = ?CHILD(bigwig_http, worker), + + Specs = [ Http ], + + {ok, { {one_for_one, 5, 10}, Specs} }. + diff --git a/apps/bigwig/src/mochijson2.erl b/apps/bigwig/src/mochijson2.erl new file mode 100644 index 0000000..954a07d --- /dev/null +++ b/apps/bigwig/src/mochijson2.erl @@ -0,0 +1,849 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works +%% with binaries as strings, arrays as lists (without an {array, _}) +%% wrapper and it only knows how to decode UTF-8 (and ASCII). +%% +%% JSON terms are decoded as follows (javascript -> erlang): +%% +%% +%% The encoder will accept the same format that the decoder will produce, +%% but will also allow additional cases for leniency: +%% + +-module(mochijson2). +-author('bob@mochimedia.com'). +-export([encoder/1, encode/1]). +-export([decoder/1, decode/1]). + +% This is a macro to placate syntax highlighters.. +-define(Q, $\"). +-define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset, + column=N+S#decoder.column}). +-define(INC_COL(S), S#decoder{offset=1+S#decoder.offset, + column=1+S#decoder.column}). +-define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset, + column=1, + line=1+S#decoder.line}). +-define(INC_CHAR(S, C), + case C of + $\n -> + S#decoder{column=1, + line=1+S#decoder.line, + offset=1+S#decoder.offset}; + _ -> + S#decoder{column=1+S#decoder.column, + offset=1+S#decoder.offset} + end). +-define(IS_WHITESPACE(C), + (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). + +%% @type iolist() = [char() | binary() | iolist()] +%% @type iodata() = iolist() | binary() +%% @type json_string() = atom | binary() +%% @type json_number() = integer() | float() +%% @type json_array() = [json_term()] +%% @type json_object() = {struct, [{json_string(), json_term()}]} +%% @type json_iolist() = {json, iolist()} +%% @type json_term() = json_string() | json_number() | json_array() | +%% json_object() | json_iolist() + +-record(encoder, {handler=null, + utf8=false}). + +-record(decoder, {object_hook=null, + offset=0, + line=1, + column=1, + state=null}). + +%% @spec encoder([encoder_option()]) -> function() +%% @doc Create an encoder/1 with the given options. +%% @type encoder_option() = handler_option() | utf8_option() +%% @type utf8_option() = boolean(). Emit unicode as utf8 (default - false) +encoder(Options) -> + State = parse_encoder_options(Options, #encoder{}), + fun (O) -> json_encode(O, State) end. + +%% @spec encode(json_term()) -> iolist() +%% @doc Encode the given as JSON to an iolist. +encode(Any) -> + json_encode(Any, #encoder{}). + +%% @spec decoder([decoder_option()]) -> function() +%% @doc Create a decoder/1 with the given options. +decoder(Options) -> + State = parse_decoder_options(Options, #decoder{}), + fun (O) -> json_decode(O, State) end. + +%% @spec decode(iolist()) -> json_term() +%% @doc Decode the given iolist to Erlang terms. +decode(S) -> + json_decode(S, #decoder{}). + +%% Internal API + +parse_encoder_options([], State) -> + State; +parse_encoder_options([{handler, Handler} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{handler=Handler}); +parse_encoder_options([{utf8, Switch} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{utf8=Switch}). + +parse_decoder_options([], State) -> + State; +parse_decoder_options([{object_hook, Hook} | Rest], State) -> + parse_decoder_options(Rest, State#decoder{object_hook=Hook}). + +json_encode(true, _State) -> + <<"true">>; +json_encode(false, _State) -> + <<"false">>; +json_encode(null, _State) -> + <<"null">>; +json_encode(I, _State) when is_integer(I) -> + integer_to_list(I); +json_encode(F, _State) when is_float(F) -> + mochinum:digits(F); +json_encode(S, State) when is_binary(S); is_atom(S) -> + json_encode_string(S, State); +json_encode([{K, _}|_] = Props, State) when (K =/= struct andalso + K =/= array andalso + K =/= json) -> + json_encode_proplist(Props, State); +json_encode({struct, Props}, State) when is_list(Props) -> + json_encode_proplist(Props, State); +json_encode(Array, State) when is_list(Array) -> + json_encode_array(Array, State); +json_encode({array, Array}, State) when is_list(Array) -> + json_encode_array(Array, State); +json_encode({json, IoList}, _State) -> + IoList; +json_encode(Bad, #encoder{handler=null}) -> + exit({json_encode, {bad_term, Bad}}); +json_encode(Bad, State=#encoder{handler=Handler}) -> + json_encode(Handler(Bad), State). + +json_encode_array([], _State) -> + <<"[]">>; +json_encode_array(L, State) -> + F = fun (O, Acc) -> + [$,, json_encode(O, State) | Acc] + end, + [$, | Acc1] = lists:foldl(F, "[", L), + lists:reverse([$\] | Acc1]). + +json_encode_proplist([], _State) -> + <<"{}">>; +json_encode_proplist(Props, State) -> + F = fun ({K, V}, Acc) -> + KS = json_encode_string(K, State), + VS = json_encode(V, State), + [$,, VS, $:, KS | Acc] + end, + [$, | Acc1] = lists:foldl(F, "{", Props), + lists:reverse([$\} | Acc1]). + +json_encode_string(A, State) when is_atom(A) -> + L = atom_to_list(A), + case json_string_is_safe(L) of + true -> + [?Q, L, ?Q]; + false -> + json_encode_string_unicode(xmerl_ucs:from_utf8(L), State, [?Q]) + end; +json_encode_string(B, State) when is_binary(B) -> + case json_bin_is_safe(B) of + true -> + [?Q, B, ?Q]; + false -> + json_encode_string_unicode(xmerl_ucs:from_utf8(B), State, [?Q]) + end; +json_encode_string(I, _State) when is_integer(I) -> + [?Q, integer_to_list(I), ?Q]; +json_encode_string(L, State) when is_list(L) -> + case json_string_is_safe(L) of + true -> + [?Q, L, ?Q]; + false -> + json_encode_string_unicode(L, State, [?Q]) + end. + +json_string_is_safe([]) -> + true; +json_string_is_safe([C | Rest]) -> + case C of + ?Q -> + false; + $\\ -> + false; + $\b -> + false; + $\f -> + false; + $\n -> + false; + $\r -> + false; + $\t -> + false; + C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF -> + false; + C when C < 16#7f -> + json_string_is_safe(Rest); + _ -> + false + end. + +json_bin_is_safe(<<>>) -> + true; +json_bin_is_safe(<>) -> + case C of + ?Q -> + false; + $\\ -> + false; + $\b -> + false; + $\f -> + false; + $\n -> + false; + $\r -> + false; + $\t -> + false; + C when C >= 0, C < $\s; C >= 16#7f -> + false; + C when C < 16#7f -> + json_bin_is_safe(Rest) + end. + +json_encode_string_unicode([], _State, Acc) -> + lists:reverse([$\" | Acc]); +json_encode_string_unicode([C | Cs], State, Acc) -> + Acc1 = case C of + ?Q -> + [?Q, $\\ | Acc]; + %% Escaping solidus is only useful when trying to protect + %% against "" injection attacks which are only + %% possible when JSON is inserted into a HTML document + %% in-line. mochijson2 does not protect you from this, so + %% if you do insert directly into HTML then you need to + %% uncomment the following case or escape the output of encode. + %% + %% $/ -> + %% [$/, $\\ | Acc]; + %% + $\\ -> + [$\\, $\\ | Acc]; + $\b -> + [$b, $\\ | Acc]; + $\f -> + [$f, $\\ | Acc]; + $\n -> + [$n, $\\ | Acc]; + $\r -> + [$r, $\\ | Acc]; + $\t -> + [$t, $\\ | Acc]; + C when C >= 0, C < $\s -> + [unihex(C) | Acc]; + C when C >= 16#7f, C =< 16#10FFFF, State#encoder.utf8 -> + [xmerl_ucs:to_utf8(C) | Acc]; + C when C >= 16#7f, C =< 16#10FFFF, not State#encoder.utf8 -> + [unihex(C) | Acc]; + C when C < 16#7f -> + [C | Acc]; + _ -> + exit({json_encode, {bad_char, C}}) + end, + json_encode_string_unicode(Cs, State, Acc1). + +hexdigit(C) when C >= 0, C =< 9 -> + C + $0; +hexdigit(C) when C =< 15 -> + C + $a - 10. + +unihex(C) when C < 16#10000 -> + <> = <>, + Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]], + [$\\, $u | Digits]; +unihex(C) when C =< 16#10FFFF -> + N = C - 16#10000, + S1 = 16#d800 bor ((N bsr 10) band 16#3ff), + S2 = 16#dc00 bor (N band 16#3ff), + [unihex(S1), unihex(S2)]. + +json_decode(L, S) when is_list(L) -> + json_decode(iolist_to_binary(L), S); +json_decode(B, S) -> + {Res, S1} = decode1(B, S), + {eof, _} = tokenize(B, S1#decoder{state=trim}), + Res. + +decode1(B, S=#decoder{state=null}) -> + case tokenize(B, S#decoder{state=any}) of + {{const, C}, S1} -> + {C, S1}; + {start_array, S1} -> + decode_array(B, S1); + {start_object, S1} -> + decode_object(B, S1) + end. + +make_object(V, #decoder{object_hook=null}) -> + V; +make_object(V, #decoder{object_hook=Hook}) -> + Hook(V). + +decode_object(B, S) -> + decode_object(B, S#decoder{state=key}, []). + +decode_object(B, S=#decoder{state=key}, Acc) -> + case tokenize(B, S) of + {end_object, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, S1#decoder{state=null}}; + {{const, K}, S1} -> + {colon, S2} = tokenize(B, S1), + {V, S3} = decode1(B, S2#decoder{state=null}), + decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc]) + end; +decode_object(B, S=#decoder{state=comma}, Acc) -> + case tokenize(B, S) of + {end_object, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, S1#decoder{state=null}}; + {comma, S1} -> + decode_object(B, S1#decoder{state=key}, Acc) + end. + +decode_array(B, S) -> + decode_array(B, S#decoder{state=any}, []). + +decode_array(B, S=#decoder{state=any}, Acc) -> + case tokenize(B, S) of + {end_array, S1} -> + {lists:reverse(Acc), S1#decoder{state=null}}; + {start_array, S1} -> + {Array, S2} = decode_array(B, S1), + decode_array(B, S2#decoder{state=comma}, [Array | Acc]); + {start_object, S1} -> + {Array, S2} = decode_object(B, S1), + decode_array(B, S2#decoder{state=comma}, [Array | Acc]); + {{const, Const}, S1} -> + decode_array(B, S1#decoder{state=comma}, [Const | Acc]) + end; +decode_array(B, S=#decoder{state=comma}, Acc) -> + case tokenize(B, S) of + {end_array, S1} -> + {lists:reverse(Acc), S1#decoder{state=null}}; + {comma, S1} -> + decode_array(B, S1#decoder{state=any}, Acc) + end. + +tokenize_string(B, S=#decoder{offset=O}) -> + case tokenize_string_fast(B, O) of + {escape, O1} -> + Length = O1 - O, + S1 = ?ADV_COL(S, Length), + <<_:O/binary, Head:Length/binary, _/binary>> = B, + tokenize_string(B, S1, lists:reverse(binary_to_list(Head))); + O1 -> + Length = O1 - O, + <<_:O/binary, String:Length/binary, ?Q, _/binary>> = B, + {{const, String}, ?ADV_COL(S, Length + 1)} + end. + +tokenize_string_fast(B, O) -> + case B of + <<_:O/binary, ?Q, _/binary>> -> + O; + <<_:O/binary, $\\, _/binary>> -> + {escape, O}; + <<_:O/binary, C1, _/binary>> when C1 < 128 -> + tokenize_string_fast(B, 1 + O); + <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223, + C2 >= 128, C2 =< 191 -> + tokenize_string_fast(B, 2 + O); + <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239, + C2 >= 128, C2 =< 191, + C3 >= 128, C3 =< 191 -> + tokenize_string_fast(B, 3 + O); + <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244, + C2 >= 128, C2 =< 191, + C3 >= 128, C3 =< 191, + C4 >= 128, C4 =< 191 -> + tokenize_string_fast(B, 4 + O); + _ -> + throw(invalid_utf8) + end. + +tokenize_string(B, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, ?Q, _/binary>> -> + {{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)}; + <<_:O/binary, "\\\"", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]); + <<_:O/binary, "\\\\", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]); + <<_:O/binary, "\\/", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]); + <<_:O/binary, "\\b", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]); + <<_:O/binary, "\\f", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]); + <<_:O/binary, "\\n", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]); + <<_:O/binary, "\\r", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]); + <<_:O/binary, "\\t", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]); + <<_:O/binary, "\\u", C3, C2, C1, C0, Rest/binary>> -> + C = erlang:list_to_integer([C3, C2, C1, C0], 16), + if C > 16#D7FF, C < 16#DC00 -> + %% coalesce UTF-16 surrogate pair + <<"\\u", D3, D2, D1, D0, _/binary>> = Rest, + D = erlang:list_to_integer([D3,D2,D1,D0], 16), + [CodePoint] = xmerl_ucs:from_utf16be(<>), + Acc1 = lists:reverse(xmerl_ucs:to_utf8(CodePoint), Acc), + tokenize_string(B, ?ADV_COL(S, 12), Acc1); + true -> + Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc), + tokenize_string(B, ?ADV_COL(S, 6), Acc1) + end; + <<_:O/binary, C1, _/binary>> when C1 < 128 -> + tokenize_string(B, ?INC_CHAR(S, C1), [C1 | Acc]); + <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223, + C2 >= 128, C2 =< 191 -> + tokenize_string(B, ?ADV_COL(S, 2), [C2, C1 | Acc]); + <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239, + C2 >= 128, C2 =< 191, + C3 >= 128, C3 =< 191 -> + tokenize_string(B, ?ADV_COL(S, 3), [C3, C2, C1 | Acc]); + <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244, + C2 >= 128, C2 =< 191, + C3 >= 128, C3 =< 191, + C4 >= 128, C4 =< 191 -> + tokenize_string(B, ?ADV_COL(S, 4), [C4, C3, C2, C1 | Acc]); + _ -> + throw(invalid_utf8) + end. + +tokenize_number(B, S) -> + case tokenize_number(B, sign, S, []) of + {{int, Int}, S1} -> + {{const, list_to_integer(Int)}, S1}; + {{float, Float}, S1} -> + {{const, list_to_float(Float)}, S1} + end. + +tokenize_number(B, sign, S=#decoder{offset=O}, []) -> + case B of + <<_:O/binary, $-, _/binary>> -> + tokenize_number(B, int, ?INC_COL(S), [$-]); + _ -> + tokenize_number(B, int, S, []) + end; +tokenize_number(B, int, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, $0, _/binary>> -> + tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]); + <<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 -> + tokenize_number(B, int1, ?INC_COL(S), [C | Acc]) + end; +tokenize_number(B, int1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, int1, ?INC_COL(S), [C | Acc]); + _ -> + tokenize_number(B, frac, S, Acc) + end; +tokenize_number(B, frac, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 -> + tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]); + <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> + tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]); + _ -> + {{int, lists:reverse(Acc)}, S} + end; +tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]); + <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> + tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]); + _ -> + {{float, lists:reverse(Acc)}, S} + end; +tokenize_number(B, esign, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ -> + tokenize_number(B, eint, ?INC_COL(S), [C | Acc]); + _ -> + tokenize_number(B, eint, S, Acc) + end; +tokenize_number(B, eint, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]) + end; +tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]); + _ -> + {{float, lists:reverse(Acc)}, S} + end. + +tokenize(B, S=#decoder{offset=O}) -> + case B of + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + tokenize(B, ?INC_CHAR(S, C)); + <<_:O/binary, "{", _/binary>> -> + {start_object, ?INC_COL(S)}; + <<_:O/binary, "}", _/binary>> -> + {end_object, ?INC_COL(S)}; + <<_:O/binary, "[", _/binary>> -> + {start_array, ?INC_COL(S)}; + <<_:O/binary, "]", _/binary>> -> + {end_array, ?INC_COL(S)}; + <<_:O/binary, ",", _/binary>> -> + {comma, ?INC_COL(S)}; + <<_:O/binary, ":", _/binary>> -> + {colon, ?INC_COL(S)}; + <<_:O/binary, "null", _/binary>> -> + {{const, null}, ?ADV_COL(S, 4)}; + <<_:O/binary, "true", _/binary>> -> + {{const, true}, ?ADV_COL(S, 4)}; + <<_:O/binary, "false", _/binary>> -> + {{const, false}, ?ADV_COL(S, 5)}; + <<_:O/binary, "\"", _/binary>> -> + tokenize_string(B, ?INC_COL(S)); + <<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9) + orelse C =:= $- -> + tokenize_number(B, S); + <<_:O/binary>> -> + trim = S#decoder.state, + {eof, S} + end. +%% +%% Tests +%% +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + + +%% testing constructs borrowed from the Yaws JSON implementation. + +%% Create an object from a list of Key/Value pairs. + +obj_new() -> + {struct, []}. + +is_obj({struct, Props}) -> + F = fun ({K, _}) when is_binary(K) -> true end, + lists:all(F, Props). + +obj_from_list(Props) -> + Obj = {struct, Props}, + ?assert(is_obj(Obj)), + Obj. + +%% Test for equivalence of Erlang terms. +%% Due to arbitrary order of construction, equivalent objects might +%% compare unequal as erlang terms, so we need to carefully recurse +%% through aggregates (tuples and objects). + +equiv({struct, Props1}, {struct, Props2}) -> + equiv_object(Props1, Props2); +equiv(L1, L2) when is_list(L1), is_list(L2) -> + equiv_list(L1, L2); +equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2; +equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2; +equiv(A, A) when A =:= true orelse A =:= false orelse A =:= null -> true. + +%% Object representation and traversal order is unknown. +%% Use the sledgehammer and sort property lists. + +equiv_object(Props1, Props2) -> + L1 = lists:keysort(1, Props1), + L2 = lists:keysort(1, Props2), + Pairs = lists:zip(L1, L2), + true = lists:all(fun({{K1, V1}, {K2, V2}}) -> + equiv(K1, K2) and equiv(V1, V2) + end, Pairs). + +%% Recursively compare tuple elements for equivalence. + +equiv_list([], []) -> + true; +equiv_list([V1 | L1], [V2 | L2]) -> + equiv(V1, V2) andalso equiv_list(L1, L2). + +decode_test() -> + [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>), + <<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]). + +e2j_vec_test() -> + test_one(e2j_test_vec(utf8), 1). + +test_one([], _N) -> + %% io:format("~p tests passed~n", [N-1]), + ok; +test_one([{E, J} | Rest], N) -> + %% io:format("[~p] ~p ~p~n", [N, E, J]), + true = equiv(E, decode(J)), + true = equiv(E, decode(encode(E))), + test_one(Rest, 1+N). + +e2j_test_vec(utf8) -> + [ + {1, "1"}, + {3.1416, "3.14160"}, %% text representation may truncate, trail zeroes + {-1, "-1"}, + {-3.1416, "-3.14160"}, + {12.0e10, "1.20000e+11"}, + {1.234E+10, "1.23400e+10"}, + {-1.234E-10, "-1.23400e-10"}, + {10.0, "1.0e+01"}, + {123.456, "1.23456E+2"}, + {10.0, "1e1"}, + {<<"foo">>, "\"foo\""}, + {<<"foo", 5, "bar">>, "\"foo\\u0005bar\""}, + {<<"">>, "\"\""}, + {<<"\n\n\n">>, "\"\\n\\n\\n\""}, + {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""}, + {obj_new(), "{}"}, + {obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"}, + {obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]), + "{\"foo\":\"bar\",\"baz\":123}"}, + {[], "[]"}, + {[[]], "[[]]"}, + {[1, <<"foo">>], "[1,\"foo\"]"}, + + %% json array in a json object + {obj_from_list([{<<"foo">>, [123]}]), + "{\"foo\":[123]}"}, + + %% json object in a json object + {obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]), + "{\"foo\":{\"bar\":true}}"}, + + %% fold evaluation order + {obj_from_list([{<<"foo">>, []}, + {<<"bar">>, obj_from_list([{<<"baz">>, true}])}, + {<<"alice">>, <<"bob">>}]), + "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"}, + + %% json object in a json array + {[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null], + "[-123,\"foo\",{\"bar\":[]},null]"} + ]. + +%% test utf8 encoding +encoder_utf8_test() -> + %% safe conversion case (default) + [34,"\\u0001","\\u0442","\\u0435","\\u0441","\\u0442",34] = + encode(<<1,"\321\202\320\265\321\201\321\202">>), + + %% raw utf8 output (optional) + Enc = mochijson2:encoder([{utf8, true}]), + [34,"\\u0001",[209,130],[208,181],[209,129],[209,130],34] = + Enc(<<1,"\321\202\320\265\321\201\321\202">>). + +input_validation_test() -> + Good = [ + {16#00A3, <>}, %% pound + {16#20AC, <>}, %% euro + {16#10196, <>} %% denarius + ], + lists:foreach(fun({CodePoint, UTF8}) -> + Expect = list_to_binary(xmerl_ucs:to_utf8(CodePoint)), + Expect = decode(UTF8) + end, Good), + + Bad = [ + %% 2nd, 3rd, or 4th byte of a multi-byte sequence w/o leading byte + <>, + %% missing continuations, last byte in each should be 80-BF + <>, + <>, + <>, + %% we don't support code points > 10FFFF per RFC 3629 + <>, + %% escape characters trigger a different code path + <> + ], + lists:foreach( + fun(X) -> + ok = try decode(X) catch invalid_utf8 -> ok end, + %% could be {ucs,{bad_utf8_character_code}} or + %% {json_encode,{bad_char,_}} + {'EXIT', _} = (catch encode(X)) + end, Bad). + +inline_json_test() -> + ?assertEqual(<<"\"iodata iodata\"">>, + iolist_to_binary( + encode({json, [<<"\"iodata">>, " iodata\""]}))), + ?assertEqual({struct, [{<<"key">>, <<"iodata iodata">>}]}, + decode( + encode({struct, + [{key, {json, [<<"\"iodata">>, " iodata\""]}}]}))), + ok. + +big_unicode_test() -> + UTF8Seq = list_to_binary(xmerl_ucs:to_utf8(16#0001d120)), + ?assertEqual( + <<"\"\\ud834\\udd20\"">>, + iolist_to_binary(encode(UTF8Seq))), + ?assertEqual( + UTF8Seq, + decode(iolist_to_binary(encode(UTF8Seq)))), + ok. + +custom_decoder_test() -> + ?assertEqual( + {struct, [{<<"key">>, <<"value">>}]}, + (decoder([]))("{\"key\": \"value\"}")), + F = fun ({struct, [{<<"key">>, <<"value">>}]}) -> win end, + ?assertEqual( + win, + (decoder([{object_hook, F}]))("{\"key\": \"value\"}")), + ok. + +atom_test() -> + %% JSON native atoms + [begin + ?assertEqual(A, decode(atom_to_list(A))), + ?assertEqual(iolist_to_binary(atom_to_list(A)), + iolist_to_binary(encode(A))) + end || A <- [true, false, null]], + %% Atom to string + ?assertEqual( + <<"\"foo\"">>, + iolist_to_binary(encode(foo))), + ?assertEqual( + <<"\"\\ud834\\udd20\"">>, + iolist_to_binary(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))), + ok. + +key_encode_test() -> + %% Some forms are accepted as keys that would not be strings in other + %% cases + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode({struct, [{foo, 1}]}))), + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode({struct, [{<<"foo">>, 1}]}))), + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode({struct, [{"foo", 1}]}))), + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode([{foo, 1}]))), + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode([{<<"foo">>, 1}]))), + ?assertEqual( + <<"{\"foo\":1}">>, + iolist_to_binary(encode([{"foo", 1}]))), + ?assertEqual( + <<"{\"\\ud834\\udd20\":1}">>, + iolist_to_binary( + encode({struct, [{[16#0001d120], 1}]}))), + ?assertEqual( + <<"{\"1\":1}">>, + iolist_to_binary(encode({struct, [{1, 1}]}))), + ok. + +unsafe_chars_test() -> + Chars = "\"\\\b\f\n\r\t", + [begin + ?assertEqual(false, json_string_is_safe([C])), + ?assertEqual(false, json_bin_is_safe(<>)), + ?assertEqual(<>, decode(encode(<>))) + end || C <- Chars], + ?assertEqual( + false, + json_string_is_safe([16#0001d120])), + ?assertEqual( + false, + json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8(16#0001d120)))), + ?assertEqual( + [16#0001d120], + xmerl_ucs:from_utf8( + binary_to_list( + decode(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))))), + ?assertEqual( + false, + json_string_is_safe([16#110000])), + ?assertEqual( + false, + json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8([16#110000])))), + %% solidus can be escaped but isn't unsafe by default + ?assertEqual( + <<"/">>, + decode(<<"\"\\/\"">>)), + ok. + +int_test() -> + ?assertEqual(0, decode("0")), + ?assertEqual(1, decode("1")), + ?assertEqual(11, decode("11")), + ok. + +large_int_test() -> + ?assertEqual(<<"-2147483649214748364921474836492147483649">>, + iolist_to_binary(encode(-2147483649214748364921474836492147483649))), + ?assertEqual(<<"2147483649214748364921474836492147483649">>, + iolist_to_binary(encode(2147483649214748364921474836492147483649))), + ok. + +float_test() -> + ?assertEqual(<<"-2147483649.0">>, iolist_to_binary(encode(-2147483649.0))), + ?assertEqual(<<"2147483648.0">>, iolist_to_binary(encode(2147483648.0))), + ok. + +handler_test() -> + ?assertEqual( + {'EXIT',{json_encode,{bad_term,{}}}}, + catch encode({})), + F = fun ({}) -> [] end, + ?assertEqual( + <<"[]">>, + iolist_to_binary((encoder([{handler, F}]))({}))), + ok. + +-endif. diff --git a/apps/bigwig/src/myserver.erl b/apps/bigwig/src/myserver.erl new file mode 100644 index 0000000..7fdc187 --- /dev/null +++ b/apps/bigwig/src/myserver.erl @@ -0,0 +1,49 @@ +-module(myserver). +-behaviour(gen_server). +-define(SERVER, ?MODULE). + +%% ------------------------------------------------------------------ +%% API Function Exports +%% ------------------------------------------------------------------ + +-export([start_link/0]). + +%% ------------------------------------------------------------------ +%% gen_server Function Exports +%% ------------------------------------------------------------------ + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +%% ------------------------------------------------------------------ +%% API Function Definitions +%% ------------------------------------------------------------------ + +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +%% ------------------------------------------------------------------ +%% gen_server Function Definitions +%% ------------------------------------------------------------------ + +init(Args) -> + {ok, Args}. + +handle_call(_Request, _From, State) -> + {noreply, ok, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%% ------------------------------------------------------------------ +%% Internal Function Definitions +%% ------------------------------------------------------------------ + diff --git a/apps/bigwig/src/rb2.erl b/apps/bigwig/src/rb2.erl new file mode 100644 index 0000000..c9b5d48 --- /dev/null +++ b/apps/bigwig/src/rb2.erl @@ -0,0 +1,504 @@ +%% This is a bare bones version of sasl/rb, but modified to load reports and +%% return them from calls, instead of printing to stdout. +%% +%% Also, all filtering/grepping of reports has been removed for now. +%% +-module(rb2). + +-behaviour(gen_server). + +-export([start_link/1, start/0, start/1, stop/0, rescan/0, rescan/1]). +-export([load_number/1, load_list/0, load_list/1]). + +%% gen_server callbacks +-export([init/1, terminate/2, handle_call/3, + handle_cast/2, handle_info/2, code_change/3]). + +-record(state, {dir, data, device, max, type, abort, log}). + +%%----------------------------------------------------------------- +start() -> start([]). +start(Options) -> + supervisor:start_child(sasl_sup, + {rb2_server, {rb2, start_link, [Options]}, + temporary, brutal_kill, worker, [rb2]}). + +start_link(Options) -> + gen_server:start_link({local, rb2_server}, rb2, Options, []). + +stop() -> + supervisor:terminate_child(sasl_sup, rb2_server). + +rescan() -> rescan([]). +rescan(Options) -> + call({rescan, Options}). + + +load_list() -> load_list(all). + +load_list(Type) -> call({load_list, Type}). + +%% Load a report by its number +load_number(Number) when is_integer(Number) -> + call({load_number, Number}). + +%%----------------------------------------------------------------- + +call(Req) -> + gen_server:call(rb2_server, Req, infinity). + +%%----------------------------------------------------------------- + +init(Options) -> + process_flag(priority, low), + process_flag(trap_exit, true), + Log = get_option(Options, start_log, standard_io), + Device = open_log_file(Log), + Dir = get_report_dir(Options), + Max = get_option(Options, max, all), + Type = get_option(Options, type, all), + Abort = get_option(Options, abort_on_error, false), + Data = scan_files(Dir ++ "/", Max, Type), + {ok, #state{dir = Dir ++ "/", data = Data, device = Device, + max = Max, type = Type, abort = Abort, log = Log}}. + +handle_call({rescan, Options}, _From, State) -> + {Device,Log1} = + case get_option(Options, start_log, {undefined}) of + {undefined} -> + {State#state.device,State#state.log}; + Log -> + close_device(State#state.device), + {open_log_file(Log),Log} + end, + Max = get_option(Options, max, State#state.max), + Type = get_option(Options, type, State#state.type), + Abort = get_option(Options, abort_on_error, false), + Data = scan_files(State#state.dir, Max, Type), + NewState = State#state{data = Data, max = Max, type = Type, + device = Device, abort = Abort, log = Log1}, + {reply, ok, NewState}; + +handle_call(_, _From, #state{data = undefined}) -> + {reply, {error, no_data}, #state{}}; + +handle_call({load_list, WantedType}, _From, State) -> + Reports = [ {No, RealType, ShortDesc, Date} + || {No, RealType, ShortDesc, Date, _Fname, _FilePos} + <- State#state.data, + WantedType == all orelse RealType == WantedType ], + {reply, Reports, State}; + +handle_call({load_number, Number}, _From, State) -> + #state{dir = Dir, data = Data, device = Device, abort = Abort, log = Log} = State, + Ret = load_report_by_num(Dir, Data, Number, Device, Abort, Log), + {reply, Ret, State#state{device = Device}}. + +terminate(_Reason, #state{device = Device}) -> + close_device(Device). + +handle_cast(_Msg, State) -> + {noreply, State}. +handle_info(_Info, State) -> + {noreply, State}. +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%----------------------------------------------------------------- +%% Func: open_log_file/1 +%% Args: FileName | standard_io +%% Returns: A Device for later use in call to io:format +%%----------------------------------------------------------------- +open_log_file(standard_io) -> standard_io; +open_log_file(FileName) -> + case file:open(FileName, [write,append]) of + {ok, Fd} -> Fd; + Error -> + io:format("rb: Cannot open file '~s' (~w).~n", + [FileName, Error]), + io:format("rb: Using standard_io~n"), + standard_io + end. + +close_device(Fd) when is_pid(Fd) -> + catch file:close(Fd); +close_device(_) -> ok. + +get_option(Options, Key, Default) -> + case lists:keysearch(Key, 1, Options) of + {value, {_Key, Value}} -> Value; + _ -> Default + end. + +get_report_dir(Options) -> + case lists:keysearch(report_dir, 1, Options) of + {value, {_Key, RptDir}} -> RptDir; + _ -> + case catch application:get_env(sasl, error_logger_mf_dir) of + {ok, Dir} -> Dir; + _ -> + exit("cannot locate report directory") + end + end. + +%%----------------------------------------------------------------- +%% Func: scan_files(RptDir, Max, Type) +%% Args: RptDir ::= string(). +%% Max ::= integer() | all, describing how many reports +%5 to read. +%% Type ::= atom(), describing which reports to read. +%% Purpose: Scan all report files one time, and build a list of +%% small elements +%% Returns: Data, where Data is a list of +%% {Number, Type, ShortDescr, Date, Fname, FilePosition}. +%%----------------------------------------------------------------- +scan_files(RptDir, Max, Type) -> + case file:open(RptDir ++ "/index", [raw, read]) of + {ok, Fd} -> + case catch file:read(Fd, 1) of + {ok, [LastWritten]} -> + file:close(Fd), + Files = make_file_list(RptDir, LastWritten), + scan_files(RptDir, Files, Max, Type); + _X -> + file:close(Fd), + exit("cannot read the index file") + end; + _X -> exit("cannot read the index file") + end. + +make_file_list(Dir, FirstFileNo) -> + case file:list_dir(Dir) of + {ok, FileNames} -> + FileNumbers = lists:zf(fun(Name) -> + case catch list_to_integer(Name) of + Int when is_integer(Int) -> + {true, Int}; + _ -> + false + end + end, + FileNames), + shift(lists:sort(FileNumbers), FirstFileNo); + _ -> exit({bad_directory, Dir}) + end. + +shift(List, First) -> + shift(List, First, []). + +shift([H | T], H, Res) -> + [H | Res] ++ lists:reverse(T); +shift([H | T], First, Res) -> + shift(T, First, [H | Res]); +shift([], _, Res) -> + Res. + +%%----------------------------------------------------------------- +%% Func: scan_files(Dir, Files, Max, Type) +%% Args: Files is a list of FileName. +%% Purpose: Scan the report files in the index variable. +%% Returns: {Number, Type, ShortDescr, Date, FileName, FilePosition} +%%----------------------------------------------------------------- +scan_files(Dir, Files, Max, Type) -> + scan_files(Dir, 1, Files, [], Max, Type). +scan_files(_Dir, _, [], Res, _Max, _Type) -> Res; +scan_files(_Dir, _, _Files, Res, Max, _Type) when Max =< 0 -> Res; +scan_files(Dir, No, [H|T], Res, Max, Type) -> + Data = get_report_data_from_file(Dir, No, H, Max, Type), + Len = length(Data), + NewMax = dec_max(Max, Len), + NewNo = No + Len, + NewData = Data ++ Res, + scan_files(Dir, NewNo, T, NewData, NewMax, Type). + +dec_max(all, _) -> all; +dec_max(X,Y) -> X-Y. + +get_report_data_from_file(Dir, No, FileNr, Max, Type) -> + Fname = integer_to_list(FileNr), + FileName = lists:concat([Dir, Fname]), + case file:open(FileName, [read]) of + {ok, Fd} when is_pid(Fd) -> read_reports(No, Fd, Fname, Max, Type); + _ -> [{No, unknown, "Can't open file " ++ Fname, "???", Fname, 0}] + end. + +%%----------------------------------------------------------------- +%% Func: read_reports(No, Fd, Fname, Max, Type) +%% Purpose: Read reports from one report file. +%% Returns: A list of {No, Type, ShortDescr, Date, FileName, FilePosition} +%% Note: We have to read all reports, and then check the max- +%% variable, because the reports are reversed on the file, and +%% we may need the last ones. +%%----------------------------------------------------------------- +read_reports(No, Fd, Fname, Max, Type) -> + io:format("rb: reading report..."), + case catch read_reports(Fd, [], Type) of + {ok, Res} -> + file:close(Fd), + io:format("done.~n"), + NewRes = + if + length(Res) > Max -> + lists:sublist(Res, 1, Max); + true -> + Res + end, + add_report_data(NewRes, No, Fname); + {error, [Problem | Res]} -> + file:close(Fd), + io:format("Error: ~p~n",[Problem]), + io:format("Salvaged ~p entries from corrupt report file ~s...~n", + [length(Res),Fname]), + NewRes = + if + length([Problem|Res]) > Max -> + lists:sublist([Problem|Res], 1, Max); + true -> + [Problem|Res] + end, + add_report_data(NewRes, No, Fname); + Else -> + io:format("err ~p~n", [Else]), + [{No, unknown, "Can't read reports from file " ++ Fname, + "???", Fname, 0}] + end. + +read_reports(Fd, Res, Type) -> + {ok, FilePos} = file:position(Fd, cur), + case catch read_report(Fd) of + {ok, Report} -> + RealType = get_type(Report), + {ShortDescr, Date} = get_short_descr(Report), + Rep = {RealType, ShortDescr, Date, FilePos}, + if + Type == all-> + read_reports(Fd, [Rep | Res], Type); + RealType == Type -> + read_reports(Fd, [Rep | Res], Type); + is_list(Type) -> + case lists:member(RealType, Type) of + true -> + read_reports(Fd, [Rep | Res], Type); + _ -> + read_reports(Fd, Res, Type) + end; + true -> + read_reports(Fd, Res, Type) + end; + {error, Error} -> + {error, [{unknown, Error, [], FilePos} | Res]}; + eof -> + {ok, Res}; + {'EXIT', Reason} -> + [{unknown, Reason, [], FilePos} | Res] + end. + +read_report(Fd) -> + case io:get_chars(Fd,'',2) of + [Hi,Lo] -> + Size = get_int16(Hi,Lo), + case io:get_chars(Fd,'',Size) of + eof -> + {error,"Premature end of file"}; + List -> + Bin = list_to_binary(List), + Ref = make_ref(), + case (catch {Ref,binary_to_term(Bin)}) of + {'EXIT',_} -> + {error, "Incomplete erlang term in log"}; + {Ref,Term} -> + {ok, Term} + end + end; + eof -> + eof + end. + +get_int16(Hi,Lo) -> + ((Hi bsl 8) band 16#ff00) bor (Lo band 16#ff). + +%%----------------------------------------------------------------- +%% Func: add_report_data(Res, No, FName) +%% Args: Res is a list of {Type, ShortDescr, Date, FilePos} +%% Purpose: Convert a list of {Type, ShortDescr, Date, FilePos} to +%% a list of {No, Type, ShortDescr, Date, FileName, FilePos} +%% Returns: A list of {No, Type, ShortDescr, Date, FileName, FilePos} +%%----------------------------------------------------------------- +add_report_data(Res, No, FName) -> + add_report_data(Res, No, FName, []). +add_report_data([{Type, ShortDescr, Date, FilePos}|T], No, FName, Res) -> + add_report_data(T, No+1, FName, + [{No, Type, ShortDescr, Date, FName, FilePos}|Res]); +add_report_data([], _No, _FName, Res) -> Res. + +%%----------------------------------------------------------------- +%% Update these functions with the reports that should be possible +%% to browse with rb. +%%----------------------------------------------------------------- +get_type({_Time, {error_report, _Pid, {_, crash_report, _}}}) -> + crash_report; +get_type({_Time, {error_report, _Pid, {_, supervisor_report, _}}}) -> + supervisor_report; +get_type({_Time, {info_report, _Pid, {_, progress, _}}}) -> + progress; +get_type({_Time, {Type, _, _}}) -> Type; +get_type(_) -> unknown. + +get_short_descr({{Date, Time}, {error_report, Pid, {_, crash_report, Rep}}}) -> + [OwnRep | _] = Rep, + Name = + case lists:keysearch(registered_name, 1, OwnRep) of + {value, {_Key, []}} -> + case lists:keysearch(initial_call, 1, OwnRep) of + {value, {_K, {M,_F,_A}}} -> M; + _ -> Pid + end; + {value, {_Key, N}} -> N; + _ -> Pid + end, + NameStr = lists:flatten(io_lib:format("~w", [Name])), + {NameStr, date_str(Date, Time)}; +get_short_descr({{Date, Time}, {error_report, Pid, {_, supervisor_report,Rep}}}) -> + Name = + case lists:keysearch(supervisor, 1, Rep) of + {value, {_Key, N}} when is_atom(N) -> N; + _ -> Pid + end, + NameStr = lists:flatten(io_lib:format("~w", [Name])), + {NameStr, date_str(Date,Time)}; +get_short_descr({{Date, Time}, {_Type, Pid, _}}) -> + NameStr = lists:flatten(io_lib:format("~w", [Pid])), + {NameStr, date_str(Date,Time)}; +get_short_descr(_) -> + {"???", "???"}. + +date_str({Y,Mo,D}=Date,{H,Mi,S}=Time) -> + case application:get_env(sasl,utc_log) of + {ok,true} -> + {{YY,MoMo,DD},{HH,MiMi,SS}} = + local_time_to_universal_time({Date,Time}), + lists:flatten(io_lib:format("~w-~2.2.0w-~2.2.0w ~2.2.0w:" + "~2.2.0w:~2.2.0w UTC", + [YY,MoMo,DD,HH,MiMi,SS])); + _ -> + lists:flatten(io_lib:format("~w-~2.2.0w-~2.2.0w ~2.2.0w:" + "~2.2.0w:~2.2.0w", + [Y,Mo,D,H,Mi,S])) + end. + +local_time_to_universal_time({Date,Time}) -> + case calendar:local_time_to_universal_time_dst({Date,Time}) of + [UCT] -> + UCT; + [UCT1,_UCT2] -> + UCT1; + [] -> % should not happen + {Date,Time} + end. + + +load_report_by_num(Dir, Data, Number, Device, Abort, Log) -> + case find_report(Data, Number) of + {Fname, FilePosition} -> + FileName = lists:concat([Dir, Fname]), + case file:open(FileName, [read]) of + {ok, Fd} -> + case load_rep(Fd, FilePosition, Device, Abort, Log) of + {ok, Date, Msg} -> + {ok, Date, Msg}; + Err -> + {error, Err} + end; + _ -> + Msg = io_lib:format("rb: can't open file ~p~n", [Fname]), + {{error, Msg},Device} + end; + no_report -> + {{error, no_report},Device} + end. + +find_report([{No, _Type, _Descr, _Date, Fname, FilePosition}|_T], No) -> + {Fname, FilePosition}; +find_report([_H|T], No) -> + find_report(T, No); +find_report([], No) -> + io:format("There is no report with number ~p.~n", [No]), + no_report. + +load_rep(Fd, FilePosition, _Device, _Abort, _Log) -> + case read_rep_msg(Fd, FilePosition) of + {Date, Msg} -> {ok, Date, fmt_report(Msg)}; + _ -> {error, "rb: Cannot read from file"} + end. + +read_rep_msg(Fd, FilePosition) -> + file:position(Fd, {bof, FilePosition}), + Res = + case catch read_report(Fd) of + {ok, Report} -> + {_ShortDescr, Date} = get_short_descr(Report), + {Date, Report}; + _ -> error + end, + file:close(Fd), + Res. + + +%% report formatring stuff + +fmt_report({Date, {ReportLevel, GL, {Pid, CrashType, CrashReport}}}) -> + [ {report_level, ReportLevel}, + {group_leader, GL}, + {date, Date}, + {pid, Pid}, + {report_type, CrashType} + | format_crashreport(CrashType, CrashReport) + ]; + +fmt_report({Date, {ReportLevel, GL, GenericReport}}) -> + [ {report_level, ReportLevel}, + {group_leader, GL}, + {date, Date}, + {generic_data, lists:flatten(io_lib:format("~p",[GenericReport]))} + ]; + +fmt_report({Date, UnknownReport}) -> + [ {generic_data, lists:flatten(io_lib:format("~p",[UnknownReport]))}, + {date, Date} + ]. + + +format_crashreport(crash_report, [OwnReport, LinkReport]) -> + [ {crashing_process, OwnReport}, + {neighbours, LinkReport} + ]; + +format_crashreport(supervisor_report, Data) -> + SuperName = get_opt(supervisor, Data), + ErrorContext = get_opt(errorContext, Data), + Reason = get_opt(reason, Data), + ChildInfo = get_opt(offender, Data), + [ {supervisor_name, SuperName}, + {error_context, ErrorContext}, + {reason, Reason}, + {child_info, lists:map(fun(CI) -> transform_mfa(CI) end, ChildInfo)} + ]; + +format_crashreport(_, Data) -> + [ + {data, Data} + ]. + +transform_mfa({mfa, Value}) -> {start_function, Value}; +transform_mfa(X) -> X. + +get_opt(Key, List) -> + case lists:keysearch(Key, 1, List) of + {value, {_Key, Val}} -> Val; + _ -> undefined + end. + + + + + diff --git a/apps/bigwig/src/rb_format_supp.erl b/apps/bigwig/src/rb_format_supp.erl new file mode 100644 index 0000000..b1d83d1 --- /dev/null +++ b/apps/bigwig/src/rb_format_supp.erl @@ -0,0 +1,155 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(rb_format_supp). + +%% user interface +-export([print/3]). + +%%----------------------------------------------------------------- +%% This module prints error reports. Called from rb. +%%----------------------------------------------------------------- + +print(Date, Report, Device) -> + Line = 79, +%% Remove these comments when we can run rb in erl44!!! +% case catch sasl_report:write_report(Device, Report) of +% true -> ok; +% _ -> + {_Time, Rep} = Report, + case Rep of + {error_report, _GL, {Pid, crash_report, CrashReport}} -> + Header = format_h(Line, "CRASH REPORT", Pid, Date), + format_lib_supp:print_info(Device, + Line, + [{header, Header} | + format_c(CrashReport)]), + true; + {error_report, _GL, {Pid, supervisor_report, SupReport}} -> + Header = format_h(Line, "SUPERVISOR REPORT", Pid, Date), + format_lib_supp:print_info(Device, + Line, + [{header, Header} | + format_s(SupReport)]), + true; + {error_report, _GL, {Pid, _Type, Report1}} -> + Header = format_h(Line, "ERROR REPORT", Pid, Date), + format_lib_supp:print_info(Device, + Line, + [{header, Header}, + {data, Report1}]), + true; + {info_report, _GL, {Pid, progress, SupProgress}} -> + Header = format_h(Line, "PROGRESS REPORT", Pid, Date), + format_lib_supp:print_info(Device, + Line, + [{header, Header} | + format_p(SupProgress)]); + {info_report, _GL, {Pid, _Type, Report1}} -> + Header = format_h(Line, "INFO REPORT", Pid, Date), + format_lib_supp:print_info(Device, + Line, + [{header, Header}, + {data, Report1}]), + true; + {warning_report, _GL, {Pid, _Type, Report1}} -> + Header = format_h(Line, "WARNING REPORT", Pid, Date), + format_lib_supp:print_info(Device, + Line, + [{header, Header}, + {data, Report1}]), + true; + {error, _GL, {Pid, Format, Args}} -> + Header = format_h(Line, "ERROR REPORT", Pid, Date), + format_lib_supp:print_info(Device, + Line, + [{header, Header}]), + io:format(Device, Format, Args); + {info_msg, _GL, {Pid, Format, Args}} -> + Header = format_h(Line, "INFO REPORT", Pid, Date), + format_lib_supp:print_info(Device, + Line, + [{header, Header}]), + io:format(Device, Format, Args); + {warning_msg, _GL, {Pid, Format, Args}} -> + Header = format_h(Line, "WARNING REPORT", Pid, Date), + format_lib_supp:print_info(Device, + Line, + [{header, Header}]), + io:format(Device, Format, Args); + {Type, _GL, TypeReport} -> + io:format(Device, "~nInfo type <~w> ~s~n", + [Type, Date]), + io:format(Device, "~p", [TypeReport]); + _ -> + io:format("~nPrinting info of unknown type... ~s~n", + [Date]), + io:format(Device, "~p", [Report]) +% end + end. + +format_h(Line, Header, Pid, Date) -> + NHeader = lists:flatten(io_lib:format("~s ~w", [Header, Pid])), + DateLen = length(Date), + HeaderLen = Line - DateLen, + Format = lists:concat(["~-", HeaderLen, "s~", DateLen, "s"]), + io_lib:format(Format, [NHeader, Date]). + + +%%----------------------------------------------------------------- +%% Crash report +%%----------------------------------------------------------------- +format_c([OwnReport, LinkReport]) -> + [{items, {"Crashing process", OwnReport}}, + format_neighbours(LinkReport)]. + +format_neighbours([Data| Rest]) -> + [{newline, 1}, + {items, {"Neighbour process", Data}} | + format_neighbours(Rest)]; +format_neighbours([]) -> []. + +%%----------------------------------------------------------------- +%% Supervisor report +%%----------------------------------------------------------------- +format_s(Data) -> + SuperName = get_opt(supervisor, Data), + ErrorContext = get_opt(errorContext, Data), + Reason = get_opt(reason, Data), + ChildInfo = get_opt(offender, Data), + [{data, [{"Reporting supervisor", SuperName}]}, + {newline, 1}, + {items, {"Child process", + [{errorContext, ErrorContext}, + {reason, Reason} | + lists:map(fun(CI) -> transform_mfa(CI) end, ChildInfo)]}}]. + +transform_mfa({mfa, Value}) -> {start_function, Value}; +transform_mfa(X) -> X. + +%%----------------------------------------------------------------- +%% Progress report +%%----------------------------------------------------------------- +format_p(Data) -> + [{data, Data}]. + +get_opt(Key, List) -> + case lists:keysearch(Key, 1, List) of + {value, {_Key, Val}} -> Val; + _ -> undefined + end. diff --git a/apps/bigwig/src/reloader.erl b/apps/bigwig/src/reloader.erl new file mode 100644 index 0000000..c0f5de8 --- /dev/null +++ b/apps/bigwig/src/reloader.erl @@ -0,0 +1,161 @@ +%% @copyright 2007 Mochi Media, Inc. +%% @author Matthew Dempsky +%% +%% @doc Erlang module for automatically reloading modified modules +%% during development. + +-module(reloader). +-author("Matthew Dempsky "). + +-include_lib("kernel/include/file.hrl"). + +-behaviour(gen_server). +-export([start/0, start_link/0]). +-export([stop/0]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([all_changed/0]). +-export([is_changed/1]). +-export([reload_modules/1]). +-record(state, {last, tref}). + +%% External API + +%% @spec start() -> ServerRet +%% @doc Start the reloader. +start() -> + gen_server:start({local, ?MODULE}, ?MODULE, [], []). + +%% @spec start_link() -> ServerRet +%% @doc Start the reloader. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +%% @spec stop() -> ok +%% @doc Stop the reloader. +stop() -> + gen_server:call(?MODULE, stop). + +%% gen_server callbacks + +%% @spec init([]) -> {ok, State} +%% @doc gen_server init, opens the server in an initial state. +init([]) -> + {ok, TRef} = timer:send_interval(timer:seconds(1), doit), + {ok, #state{last = stamp(), tref = TRef}}. + +%% @spec handle_call(Args, From, State) -> tuple() +%% @doc gen_server callback. +handle_call(stop, _From, State) -> + {stop, shutdown, stopped, State}; +handle_call(_Req, _From, State) -> + {reply, {error, badrequest}, State}. + +%% @spec handle_cast(Cast, State) -> tuple() +%% @doc gen_server callback. +handle_cast(_Req, State) -> + {noreply, State}. + +%% @spec handle_info(Info, State) -> tuple() +%% @doc gen_server callback. +handle_info(doit, State) -> + Now = stamp(), + doit(State#state.last, Now), + {noreply, State#state{last = Now}}; +handle_info(_Info, State) -> + {noreply, State}. + +%% @spec terminate(Reason, State) -> ok +%% @doc gen_server termination callback. +terminate(_Reason, State) -> + {ok, cancel} = timer:cancel(State#state.tref), + ok. + + +%% @spec code_change(_OldVsn, State, _Extra) -> State +%% @doc gen_server code_change callback (trivial). +code_change(_Vsn, State, _Extra) -> + {ok, State}. + +%% @spec reload_modules([atom()]) -> [{module, atom()} | {error, term()}] +%% @doc code:purge/1 and code:load_file/1 the given list of modules in order, +%% return the results of code:load_file/1. +reload_modules(Modules) -> + [begin code:purge(M), code:load_file(M) end || M <- Modules]. + +%% @spec all_changed() -> [atom()] +%% @doc Return a list of beam modules that have changed. +all_changed() -> + [M || {M, Fn} <- code:all_loaded(), is_list(Fn), is_changed(M)]. + +%% @spec is_changed(atom()) -> boolean() +%% @doc true if the loaded module is a beam with a vsn attribute +%% and does not match the on-disk beam file, returns false otherwise. +is_changed(M) -> + try + module_vsn(M:module_info()) =/= module_vsn(code:get_object_code(M)) + catch _:_ -> + false + end. + +%% Internal API + +module_vsn({M, Beam, _Fn}) -> + {ok, {M, Vsn}} = beam_lib:version(Beam), + Vsn; +module_vsn(L) when is_list(L) -> + {_, Attrs} = lists:keyfind(attributes, 1, L), + {_, Vsn} = lists:keyfind(vsn, 1, Attrs), + Vsn. + +doit(From, To) -> + [case file:read_file_info(Filename) of + {ok, #file_info{mtime = Mtime}} when Mtime >= From, Mtime < To -> + reload(Module); + {ok, _} -> + unmodified; + {error, enoent} -> + %% The Erlang compiler deletes existing .beam files if + %% recompiling fails. Maybe it's worth spitting out a + %% warning here, but I'd want to limit it to just once. + gone; + {error, Reason} -> + io:format("Error reading ~s's file info: ~p~n", + [Filename, Reason]), + error + end || {Module, Filename} <- code:all_loaded(), is_list(Filename)]. + +reload(Module) -> + io:format("Reloading ~p ...", [Module]), + code:purge(Module), + case code:load_file(Module) of + {module, Module} -> + io:format(" ok.~n"), + case erlang:function_exported(Module, test, 0) of + true -> + io:format(" - Calling ~p:test() ...", [Module]), + case catch Module:test() of + ok -> + io:format(" ok.~n"), + reload; + Reason -> + io:format(" fail: ~p.~n", [Reason]), + reload_but_test_failed + end; + false -> + reload + end; + {error, Reason} -> + io:format(" fail: ~p.~n", [Reason]), + error + end. + + +stamp() -> + erlang:localtime(). + +%% +%% Tests +%% +-include_lib("eunit/include/eunit.hrl"). +-ifdef(TEST). +-endif. diff --git a/apps/bigwig/src/user_default.erl b/apps/bigwig/src/user_default.erl new file mode 100644 index 0000000..d7029bd --- /dev/null +++ b/apps/bigwig/src/user_default.erl @@ -0,0 +1,138 @@ +-module(user_default). +-compile(export_all). +-import(io, [format/1]). + +super_restart(Sup,Mod) -> + io:format("Terminating ~p:~p~n", [Sup,Mod]), + A = supervisor:terminate_child(Sup, Mod), + io:format("Restarting ~p:~p~n", [Sup,Mod]), + B = supervisor:restart_child(Sup, Mod), + {A,B}. + +help() -> + shell_default:help(), + format("super_restart(Sup,Id) -- restart a child on a running supervisor\n"), + dbghelp(). + +dbghelp() -> + format("dbgtc(File) -- use dbg:trace_client() to read data from File\n"), + format("dbgon(M) -- enable dbg tracer on all funs in module M\n"), + format("dbgon(M,Fun) -- enable dbg tracer for module M and function F\n"), + format("dbgon(M,File) -- enable dbg tracer for module M and log to File\n"), + format("dbgadd(M) -- enable call tracer for module M\n"), + format("dbgadd(M,F) -- enable call tracer for function M:F\n"), + format("dbgdel(M) -- disable call tracer for module M\n"), + format("dbgdel(M,F) -- disable call tracer for function M:F\n"), + format("dbgoff() -- disable dbg tracer (calls dbg:stop/0)\n"), + format("l() -- load all changed modules\n"), + format("nl() -- load all changed modules on all known nodes\n"), + format("mm() -- list modified modules\n"), + true. + +dbgtc(File) -> + Fun = fun({trace,_,call,{M,F,A}}, _) -> + io:format("call: ~w:~w~w~n", [M,F,A]); + ({trace,_,return_from,{M,F,A},R}, _) -> + io:format("retn: ~w:~w/~w -> ~w~n", [M,F,A,R]); + (A,B) -> + io:format("~w: ~w~n", [A,B]) + end, + dbg:trace_client(file, File, {Fun, []}). + +dbgon(Module) -> + case dbg:tracer() of + {ok,_} -> + dbg:p(all,call), + dbg:tpl(Module, [{'_',[],[{return_trace}]}]), + ok; + Else -> + Else + end. + +dbgon(Module, Fun) when is_atom(Fun) -> + {ok,_} = dbg:tracer(), + dbg:p(all,call), + dbg:tpl(Module, Fun, [{'_',[],[{return_trace}]}]), + ok; + +dbgon(Module, File) when is_list(File) -> + {ok,_} = dbg:tracer(file, dbg:trace_port(file, File)), + dbg:p(all,call), + dbg:tpl(Module, [{'_',[],[{return_trace}]}]), + ok. + +dbgadd(Module) -> + dbg:tpl(Module, [{'_',[],[{return_trace}]}]), + ok. + +dbgadd(Module, Fun) -> + dbg:tpl(Module, Fun, [{'_',[],[{return_trace}]}]), + ok. + +dbgdel(Module) -> + dbg:ctpl(Module), + ok. + +dbgdel(Module, Fun) -> + dbg:ctpl(Module, Fun), + ok. + +dbgoff() -> + dbg:stop(). + + +lm() -> + [c:l(M) || M <- mm()]. + +mm() -> + modified_modules(). + +modified_modules() -> + [M || {M, _} <- code:all_loaded(), module_modified(M) == true]. + +module_modified(Module) -> + case code:is_loaded(Module) of + {file, preloaded} -> + false; + {file, Path} -> + CompileOpts = proplists:get_value(compile, Module:module_info()), + CompileTime = proplists:get_value(time, CompileOpts), + Src = proplists:get_value(source, CompileOpts), + module_modified(Path, CompileTime, Src); + _ -> + false + end. + +module_modified(Path, PrevCompileTime, PrevSrc) -> + case find_module_file(Path) of + false -> + false; + ModPath -> + case beam_lib:chunks(ModPath, ["CInf"]) of + {ok, {_, [{_, CB}]}} -> + CompileOpts = binary_to_term(CB), + CompileTime = proplists:get_value(time, CompileOpts), + Src = proplists:get_value(source, CompileOpts), + not (CompileTime == PrevCompileTime) and (Src == PrevSrc); + _ -> + false + end + end. + +find_module_file(Path) -> + case file:read_file_info(Path) of + {ok, _} -> + Path; + _ -> + %% may be the path was changed? + case code:where_is_file(filename:basename(Path)) of + non_existing -> + false; + NewPath -> + NewPath + end + end. + + +time(F) when is_function(F) -> + S = now(), Res = F(), E = now(), {timer:now_diff(E,S), Res}. diff --git a/rebar b/rebar new file mode 100755 index 0000000000000000000000000000000000000000..4b5090b3471990722bcbac188e4c7e170430508d GIT binary patch literal 102487 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

7R;nVxSWEE}n<-1cBz@iK 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

gnVW||_=%ry@@-jc0-Qb&b=?saaE12Ej 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 ZPxAx=9Gzag-~s$<11{UD07Nfd{6EC(mQw%# literal 0 HcmV?d00001 diff --git a/start-dev.sh b/start-dev.sh new file mode 100755 index 0000000..6c0429f --- /dev/null +++ b/start-dev.sh @@ -0,0 +1,4 @@ +#!/bin/sh +cd `dirname $0` +make +exec erl -sname bigwig -pa $PWD/apps/*/ebin $PWD/deps/*/ebin -boot start_sasl -config sys.config -s reloader -s bigwig diff --git a/sys.config b/sys.config new file mode 100644 index 0000000..cae2f04 --- /dev/null +++ b/sys.config @@ -0,0 +1,10 @@ +[ + %% SASL config + {sasl, [ + {sasl_error_logger, {file, "log/sasl-error.log"}}, + {errlog_type, error}, + {error_logger_mf_dir, "log/sasl"}, % Log directory + {error_logger_mf_maxbytes, 10485760}, % 10 MB max file size + {error_logger_mf_maxfiles, 5} % 5 files max + ]} +]. From f6972e94360443d0cd27bace1979a548e71c646c Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 08:47:21 +0100 Subject: [PATCH 002/405] update readme --- README | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/README b/README index b77b1fb..3ff0190 100644 --- a/README +++ b/README @@ -1,5 +1,15 @@ -bigwig is a web interface to a running erlang system. +A suite of web-based tools for the Erlang VM. -you get stats on the state of the VM, releases, apps etc -Also, a web-based report-browser, etop, appmon etc etc +Tools that we aim to offer sexy, web-based versions of include: + +* "etop" +* "rb" +* "appmon" + +Also included is a VM dashboard with release_handler details, loaded +applications, and VM settings. + + +Conceived for Spawnfest 2011, SMELLS LIKE BEAM SPIRIT. + From 6a2480519e647151ac4a505790a030d00ccbd82c Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 10:57:03 +0100 Subject: [PATCH 003/405] dispatch rules --- apps/bigwig/src/bigwig_http.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl index c218767..61534ef 100644 --- a/apps/bigwig/src/bigwig_http.erl +++ b/apps/bigwig/src/bigwig_http.erl @@ -21,9 +21,12 @@ start_link() -> init([]) -> + %% {Host, list({Path, Handler, Opts})} Dispatch = [ - %% {Host, list({Path, Handler, Opts})} - {'_', [{'_', bigwig_http_vm, []}]} + {'_', [{[<<"">>], bigwig_http_index, []}]} + + , {'_', [{[<<"vm">>], bigwig_http_vm, []}]} + , {'_', [{[<<"rb">>, '...'], bigwig_http_rb, []}]} ], Port = 8080, From 2747b78d9e54ecda93487d0221860a294ccd4600 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 11:25:59 +0100 Subject: [PATCH 004/405] fix dispatch rules --- apps/bigwig/src/bigwig_http.erl | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl index 61534ef..41c8d04 100644 --- a/apps/bigwig/src/bigwig_http.erl +++ b/apps/bigwig/src/bigwig_http.erl @@ -19,23 +19,26 @@ start_link() -> %% gen_server Function Definitions %% ------------------------------------------------------------------ -init([]) -> - +dispatch_rules() -> %% {Host, list({Path, Handler, Opts})} - Dispatch = [ - {'_', [{[<<"">>], bigwig_http_index, []}]} + [{'_', [ + + {[<<"">>], bigwig_http_index, []} + + , {[<<"vm">>], bigwig_http_vm, []} + , {[<<"rb">>, '...'], bigwig_http_rb, []} - , {'_', [{[<<"vm">>], bigwig_http_vm, []}]} - , {'_', [{[<<"rb">>, '...'], bigwig_http_rb, []}]} - ], + , {'_', bigwig_http_catchall, []} + ]}]. +init([]) -> Port = 8080, error_logger:info_msg("Starting http server on port ~p", [Port]), - + %% %% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts cowboy:start_listener(http, 100, cowboy_tcp_transport, [{port, Port}], - cowboy_http_protocol, [{dispatch, Dispatch}] + cowboy_http_protocol, [{dispatch, dispatch_rules()}] ), {ok, #state{}}. From d1464f182de3eba29a7c6c96321dc1b6bb4b7eab Mon Sep 17 00:00:00 2001 From: Steven Gravell Date: Sat, 9 Jul 2011 12:11:54 +0100 Subject: [PATCH 005/405] static --- .gitignore | 8 ++--- apps/bigwig/priv/html/index.html | 9 +++++ apps/bigwig/src/bigwig_http.erl | 2 ++ apps/bigwig/src/bigwig_http_static.erl | 50 ++++++++++++++++++++++++++ apps/bigwig/src/bigwig_http_wh.erl | 27 ++++++++++++++ 5 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 apps/bigwig/priv/html/index.html create mode 100644 apps/bigwig/src/bigwig_http_static.erl create mode 100644 apps/bigwig/src/bigwig_http_wh.erl diff --git a/.gitignore b/.gitignore index 86848fa..08599f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.* -*~ -*.beam -*.dump +apps/*/ebin/*.beam +apps/*/ebin/*.app +deps/ +*.swp diff --git a/apps/bigwig/priv/html/index.html b/apps/bigwig/priv/html/index.html new file mode 100644 index 0000000..6a470c5 --- /dev/null +++ b/apps/bigwig/priv/html/index.html @@ -0,0 +1,9 @@ + + + So Peter... what's happening? + + + +

+ + diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl index 41c8d04..f45d1da 100644 --- a/apps/bigwig/src/bigwig_http.erl +++ b/apps/bigwig/src/bigwig_http.erl @@ -25,6 +25,8 @@ dispatch_rules() -> {[<<"">>], bigwig_http_index, []} + , {[<<"static">>, '...'], bigwig_http_static, []} + , {[<<"wh">>, '...'], bigwig_http_wh, []} , {[<<"vm">>], bigwig_http_vm, []} , {[<<"rb">>, '...'], bigwig_http_rb, []} diff --git a/apps/bigwig/src/bigwig_http_static.erl b/apps/bigwig/src/bigwig_http_static.erl new file mode 100644 index 0000000..6dd6c2e --- /dev/null +++ b/apps/bigwig/src/bigwig_http_static.erl @@ -0,0 +1,50 @@ +%% +%% show details on the VM, releases, apps, etc. +%% +-module(bigwig_http_static). +-behaviour(cowboy_http_handler). +-export([init/3, handle/2, terminate/2]). + +-export([html/1, css/1, js/1]). + +-compile(export_all). + +init({tcp, http}, Req, _Opts) -> + {ok, Req, undefined_state}. + +handle(Req, State) -> + Headers = [{<<"Content-Type">>, <<"text/html">>}], + {[_|Path], _} = cowboy_http_req:path(Req), % strip <<"static">> + {ok, Body} = file(filename:join(Path)), + {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. + +terminate(_Req, _State) -> + ok. + +html(Name) -> + type("html", Name). +css(Name) -> + type("css", Name). +js(Name) -> + type("js", Name). + +type(Type, Name) -> + file(filename:join([Type, Name ++ Type])). + +file(Path) -> + Priv = priv(), + file:read_file(check(Priv, filename:join(Priv, Path))). + +priv() -> + code:priv_dir(bigwig). + +check(Priv, Path) when is_list(Priv) orelse is_list(Path) -> + check(iolist_to_binary(Priv), iolist_to_binary(Path)); +check(Priv, Path) -> + Size = byte_size(Priv), + <> = Path, + case CheckPriv of + Priv -> Path; + _ -> throw({unauthorized, Path}) + end. diff --git a/apps/bigwig/src/bigwig_http_wh.erl b/apps/bigwig/src/bigwig_http_wh.erl new file mode 100644 index 0000000..89572f5 --- /dev/null +++ b/apps/bigwig/src/bigwig_http_wh.erl @@ -0,0 +1,27 @@ +%% +%% show details on the VM, releases, apps, etc. +%% +-module(bigwig_http_wh). +-behaviour(cowboy_http_handler). +-export([init/3, handle/2, terminate/2]). + +-compile(export_all). + +init({tcp, http}, Req, _Opts) -> + {ok, Req, undefined_state}. + +handle(Req, State) -> + Headers = [{<<"Content-Type">>, <<"text/html">>}], + % todo: read file into state on init + {ok, Body} = tpl("index"), + {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. + +terminate(_Req, _State) -> + ok. + +tpl(Name) -> + file:read_file(tpl_path(Name)). + +tpl_path(Name) -> + filename:join([code:priv_dir(bigwig), "html", Name ++ ".html"]). From ef682612e83a1bdd2ae42076e683335af915542f Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 12:48:52 +0100 Subject: [PATCH 006/405] add catchall --- apps/bigwig/src/bigwig_http_catchall.erl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 apps/bigwig/src/bigwig_http_catchall.erl diff --git a/apps/bigwig/src/bigwig_http_catchall.erl b/apps/bigwig/src/bigwig_http_catchall.erl new file mode 100644 index 0000000..ea7c828 --- /dev/null +++ b/apps/bigwig/src/bigwig_http_catchall.erl @@ -0,0 +1,15 @@ +-module(bigwig_http_catchall). +-behaviour(cowboy_http_handler). +-export([init/3, handle/2, terminate/2]). + +init({tcp, http}, Req, _Opts) -> + {ok, Req, undefined_state}. + +handle(Req, State) -> + Body = <<"

404

">>, + {ok, Req2} = cowboy_http_req:reply(404, [], Body, Req), + {ok, Req2, State}. + +terminate(_Req, _State) -> + ok. + From 154c6ec1fff055f362a18ddf012a27af4e78a1e2 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 12:49:57 +0100 Subject: [PATCH 007/405] add rb webservice --- apps/bigwig/src/bigwig_http_rb.erl | 66 ++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 apps/bigwig/src/bigwig_http_rb.erl diff --git a/apps/bigwig/src/bigwig_http_rb.erl b/apps/bigwig/src/bigwig_http_rb.erl new file mode 100644 index 0000000..4e04641 --- /dev/null +++ b/apps/bigwig/src/bigwig_http_rb.erl @@ -0,0 +1,66 @@ +%% +%% report browser api +%% +-module(bigwig_http_rb). +-behaviour(cowboy_http_handler). +-export([init/3, handle/2, terminate/2]). + +init({tcp, http}, Req, _Opts) -> + rb2:start(), %% will only be started once anyway, registered name + rb2:rescan(), %% ouch + {ok, Req, undefined_state}. + +handle(Req, State) -> + {Path, Req2} = cowboy_http_req:path(Req), + handle_path(Path, Req2, State). + +%% /rb/reports +handle_path([<<"rb">>, <<"reports">>], Req, State) -> + Body = mochijson2:encode(list_reports()), + Headers = [{<<"Content-Type">>, <<"application/json">>}], + {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2, State}; + +%% /rb/reports/123 +handle_path([<<"rb">>, <<"reports">>, IdBin], Req, State) -> + Id = list_to_integer(binary_to_list(IdBin)), + Report = rb2:load_number(Id), + %% TODO convert this to JSON: + %% {ok,"2011-07-09 11:42:37", + %% [{report_level,info_report}, + %% {group_leader,<0.36.0>}, + %% {date,{{2011,7,9},{11,42,37}}}, + %% {pid,<0.39.0>}, + %% {report_type,progress}, + %% {data,[{supervisor,{local,sasl_sup}}, + %% {started,[{pid,<0.170.0>}, + %% {name,rb2_server}, + %% {mfargs,{rb2,start_link,[[]]}}, + %% {restart_type,temporary}, + %% {shutdown,brutal_kill}, + %% {child_type,worker}]}]}]} + %% + Body = io_lib:format("~p",[Report]), + Headers = [{<<"Content-Type">>, <<"application/json">>}], + {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2, State}; + +handle_path(Path, Req, State) -> + {ok, Req2} = cowboy_http_req:reply(404, [], io_lib:format("Not found: ~p", [Path]), Req), %% FIXME injection + {ok, Req2, State}. + + +terminate(_Req, _State) -> + ok. + + +list_reports() -> + {struct, + lists:map( fun({Id,Type,Pid,Date}) -> + {Id, {struct, [ {uri, list_to_binary(io_lib:format("/rb/reports/~B", [Id]))}, + {type, list_to_binary(atom_to_list(Type))}, + {pid, list_to_binary(Pid)}, + {date, list_to_binary(Date)} ]}} + end, + rb2:load_list() + )}. From 9759e1d125ba3d12d82328d26d52ad19bbff5b61 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 13:07:14 +0100 Subject: [PATCH 008/405] rb2 also returns pp'ed rendered string --- apps/bigwig/src/bigwig_http_rb.erl | 7 +++++-- apps/bigwig/src/rb2.erl | 8 +++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/bigwig/src/bigwig_http_rb.erl b/apps/bigwig/src/bigwig_http_rb.erl index 4e04641..5877622 100644 --- a/apps/bigwig/src/bigwig_http_rb.erl +++ b/apps/bigwig/src/bigwig_http_rb.erl @@ -24,7 +24,7 @@ handle_path([<<"rb">>, <<"reports">>], Req, State) -> %% /rb/reports/123 handle_path([<<"rb">>, <<"reports">>, IdBin], Req, State) -> Id = list_to_integer(binary_to_list(IdBin)), - Report = rb2:load_number(Id), + Rep = rb2:load_number(Id), %% TODO convert this to JSON: %% {ok,"2011-07-09 11:42:37", %% [{report_level,info_report}, @@ -40,7 +40,10 @@ handle_path([<<"rb">>, <<"reports">>, IdBin], Req, State) -> %% {shutdown,brutal_kill}, %% {child_type,worker}]}]}]} %% - Body = io_lib:format("~p",[Report]), + + %% TODO Report is the term that needs JSONifying + %% {ok, Date, Report, ReportStr} = Report, + Body = io_lib:format("~p",[Rep]), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), {ok, Req2, State}; diff --git a/apps/bigwig/src/rb2.erl b/apps/bigwig/src/rb2.erl index c9b5d48..6fc28ef 100644 --- a/apps/bigwig/src/rb2.erl +++ b/apps/bigwig/src/rb2.erl @@ -427,7 +427,13 @@ find_report([], No) -> load_rep(Fd, FilePosition, _Device, _Abort, _Log) -> case read_rep_msg(Fd, FilePosition) of - {Date, Msg} -> {ok, Date, fmt_report(Msg)}; + {Date, Msg} -> + Fn = "/tmp/rb_format.tmp", %% TODO this is horrid + {ok, IoDevice} = file:open(Fn, [write]), + rb_format_supp:print(Date, Msg, IoDevice), + file:close(IoDevice), + {ok, ReportBin} = file:read_file(Fn), + {ok, Date, fmt_report(Msg), ReportBin}; _ -> {error, "rb: Cannot read from file"} end. From 0a9ba435a96316db601af7cbefbe1ad616abe174 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 13:10:44 +0100 Subject: [PATCH 009/405] Remove unneeded mochijson2 and replace calls with ones to jsx --- apps/bigwig/src/bigwig_http_rb.erl | 14 +- apps/bigwig/src/bigwig_http_vm.erl | 37 +- apps/bigwig/src/mochijson2.erl | 849 ----------------------------- 3 files changed, 24 insertions(+), 876 deletions(-) delete mode 100644 apps/bigwig/src/mochijson2.erl diff --git a/apps/bigwig/src/bigwig_http_rb.erl b/apps/bigwig/src/bigwig_http_rb.erl index 5877622..28bc8c8 100644 --- a/apps/bigwig/src/bigwig_http_rb.erl +++ b/apps/bigwig/src/bigwig_http_rb.erl @@ -16,7 +16,7 @@ handle(Req, State) -> %% /rb/reports handle_path([<<"rb">>, <<"reports">>], Req, State) -> - Body = mochijson2:encode(list_reports()), + Body = jsx:term_to_json(list_reports()), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), {ok, Req2, State}; @@ -58,12 +58,12 @@ terminate(_Req, _State) -> list_reports() -> - {struct, lists:map( fun({Id,Type,Pid,Date}) -> - {Id, {struct, [ {uri, list_to_binary(io_lib:format("/rb/reports/~B", [Id]))}, - {type, list_to_binary(atom_to_list(Type))}, - {pid, list_to_binary(Pid)}, - {date, list_to_binary(Date)} ]}} + {list_to_binary(integer_to_list(Id)), + [ {uri, list_to_binary(io_lib:format("/rb/reports/~B", [Id]))}, + {type, list_to_binary(atom_to_list(Type))}, + {pid, list_to_binary(Pid)}, + {date, list_to_binary(Date)} ]} end, rb2:load_list() - )}. + ). diff --git a/apps/bigwig/src/bigwig_http_vm.erl b/apps/bigwig/src/bigwig_http_vm.erl index 07f2903..68f9878 100644 --- a/apps/bigwig/src/bigwig_http_vm.erl +++ b/apps/bigwig/src/bigwig_http_vm.erl @@ -11,7 +11,7 @@ init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req, State) -> - Body = mochijson2:encode(all()), + Body = jsx:term_to_json(all()), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), {ok, Req2, State}. @@ -21,11 +21,9 @@ terminate(_Req, _State) -> all() -> - {struct, [ - {system_info, {struct, system_info()}}, - {releases, {struct, releases()}}, - {applications,{struct, applications()}} - ]}. + [{system_info, system_info()}, + {releases, releases()}, + {applications,applications()}]. %% Funs to load data about aspects of the system, mochiJSON formatted: @@ -36,7 +34,7 @@ system_info() -> (V) when is_atom(V) -> V end, Keys = [ - process_count, + process_count, process_limit, kernel_poll, logical_processors, @@ -52,12 +50,12 @@ releases() -> %% TODO parse out the name/version of the apps? FmtDeps = fun(L) -> [ list_to_binary(A) || A <- L ] end, [ - {list_to_binary(Name), {struct, - [ - {version, list_to_binary(Version)}, - {status, list_to_binary(atom_to_list(Status))}, - {deps, FmtDeps(Deps)} - ]}} + {list_to_binary(Name), + [ + {version, list_to_binary(Version)}, + {status, list_to_binary(atom_to_list(Status))}, + {deps, FmtDeps(Deps)} + ]} || {Name, Version, Deps, Status} <- Rels ]. @@ -71,17 +69,16 @@ applications() -> end, Loaded0), Format = fun(AppList) -> - {struct, - [ {Name, - {struct, [ {description, list_to_binary(Desc)}, - {version, list_to_binary(Ver)} ]} } - || {Name, Desc, Ver} <- AppList - ]} + [ {Name, + [ {description, list_to_binary(Desc)}, + {version, list_to_binary(Ver)} ]} + || {Name, Desc, Ver} <- AppList + ] end, [ {running, Format(Which)}, - {loaded, Format(Loaded)} + {loaded, Format(Loaded)} ]. diff --git a/apps/bigwig/src/mochijson2.erl b/apps/bigwig/src/mochijson2.erl deleted file mode 100644 index 954a07d..0000000 --- a/apps/bigwig/src/mochijson2.erl +++ /dev/null @@ -1,849 +0,0 @@ -%% @author Bob Ippolito -%% @copyright 2007 Mochi Media, Inc. - -%% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works -%% with binaries as strings, arrays as lists (without an {array, _}) -%% wrapper and it only knows how to decode UTF-8 (and ASCII). -%% -%% JSON terms are decoded as follows (javascript -> erlang): -%%
    -%%
  • {"key": "value"} -> -%% {struct, [{<<"key">>, <<"value">>}]}
  • -%%
  • ["array", 123, 12.34, true, false, null] -> -%% [<<"array">>, 123, 12.34, true, false, null] -%%
  • -%%
-%%
    -%%
  • Strings in JSON decode to UTF-8 binaries in Erlang
  • -%%
  • Objects decode to {struct, PropList}
  • -%%
  • Numbers decode to integer or float
  • -%%
  • true, false, null decode to their respective terms.
  • -%%
-%% The encoder will accept the same format that the decoder will produce, -%% but will also allow additional cases for leniency: -%%
    -%%
  • atoms other than true, false, null will be considered UTF-8 -%% strings (even as a proplist key) -%%
  • -%%
  • {json, IoList} will insert IoList directly into the output -%% with no validation -%%
  • -%%
  • {array, Array} will be encoded as Array -%% (legacy mochijson style) -%%
  • -%%
  • A non-empty raw proplist will be encoded as an object as long -%% as the first pair does not have an atom key of json, struct, -%% or array -%%
  • -%%
- --module(mochijson2). --author('bob@mochimedia.com'). --export([encoder/1, encode/1]). --export([decoder/1, decode/1]). - -% This is a macro to placate syntax highlighters.. --define(Q, $\"). --define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset, - column=N+S#decoder.column}). --define(INC_COL(S), S#decoder{offset=1+S#decoder.offset, - column=1+S#decoder.column}). --define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset, - column=1, - line=1+S#decoder.line}). --define(INC_CHAR(S, C), - case C of - $\n -> - S#decoder{column=1, - line=1+S#decoder.line, - offset=1+S#decoder.offset}; - _ -> - S#decoder{column=1+S#decoder.column, - offset=1+S#decoder.offset} - end). --define(IS_WHITESPACE(C), - (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). - -%% @type iolist() = [char() | binary() | iolist()] -%% @type iodata() = iolist() | binary() -%% @type json_string() = atom | binary() -%% @type json_number() = integer() | float() -%% @type json_array() = [json_term()] -%% @type json_object() = {struct, [{json_string(), json_term()}]} -%% @type json_iolist() = {json, iolist()} -%% @type json_term() = json_string() | json_number() | json_array() | -%% json_object() | json_iolist() - --record(encoder, {handler=null, - utf8=false}). - --record(decoder, {object_hook=null, - offset=0, - line=1, - column=1, - state=null}). - -%% @spec encoder([encoder_option()]) -> function() -%% @doc Create an encoder/1 with the given options. -%% @type encoder_option() = handler_option() | utf8_option() -%% @type utf8_option() = boolean(). Emit unicode as utf8 (default - false) -encoder(Options) -> - State = parse_encoder_options(Options, #encoder{}), - fun (O) -> json_encode(O, State) end. - -%% @spec encode(json_term()) -> iolist() -%% @doc Encode the given as JSON to an iolist. -encode(Any) -> - json_encode(Any, #encoder{}). - -%% @spec decoder([decoder_option()]) -> function() -%% @doc Create a decoder/1 with the given options. -decoder(Options) -> - State = parse_decoder_options(Options, #decoder{}), - fun (O) -> json_decode(O, State) end. - -%% @spec decode(iolist()) -> json_term() -%% @doc Decode the given iolist to Erlang terms. -decode(S) -> - json_decode(S, #decoder{}). - -%% Internal API - -parse_encoder_options([], State) -> - State; -parse_encoder_options([{handler, Handler} | Rest], State) -> - parse_encoder_options(Rest, State#encoder{handler=Handler}); -parse_encoder_options([{utf8, Switch} | Rest], State) -> - parse_encoder_options(Rest, State#encoder{utf8=Switch}). - -parse_decoder_options([], State) -> - State; -parse_decoder_options([{object_hook, Hook} | Rest], State) -> - parse_decoder_options(Rest, State#decoder{object_hook=Hook}). - -json_encode(true, _State) -> - <<"true">>; -json_encode(false, _State) -> - <<"false">>; -json_encode(null, _State) -> - <<"null">>; -json_encode(I, _State) when is_integer(I) -> - integer_to_list(I); -json_encode(F, _State) when is_float(F) -> - mochinum:digits(F); -json_encode(S, State) when is_binary(S); is_atom(S) -> - json_encode_string(S, State); -json_encode([{K, _}|_] = Props, State) when (K =/= struct andalso - K =/= array andalso - K =/= json) -> - json_encode_proplist(Props, State); -json_encode({struct, Props}, State) when is_list(Props) -> - json_encode_proplist(Props, State); -json_encode(Array, State) when is_list(Array) -> - json_encode_array(Array, State); -json_encode({array, Array}, State) when is_list(Array) -> - json_encode_array(Array, State); -json_encode({json, IoList}, _State) -> - IoList; -json_encode(Bad, #encoder{handler=null}) -> - exit({json_encode, {bad_term, Bad}}); -json_encode(Bad, State=#encoder{handler=Handler}) -> - json_encode(Handler(Bad), State). - -json_encode_array([], _State) -> - <<"[]">>; -json_encode_array(L, State) -> - F = fun (O, Acc) -> - [$,, json_encode(O, State) | Acc] - end, - [$, | Acc1] = lists:foldl(F, "[", L), - lists:reverse([$\] | Acc1]). - -json_encode_proplist([], _State) -> - <<"{}">>; -json_encode_proplist(Props, State) -> - F = fun ({K, V}, Acc) -> - KS = json_encode_string(K, State), - VS = json_encode(V, State), - [$,, VS, $:, KS | Acc] - end, - [$, | Acc1] = lists:foldl(F, "{", Props), - lists:reverse([$\} | Acc1]). - -json_encode_string(A, State) when is_atom(A) -> - L = atom_to_list(A), - case json_string_is_safe(L) of - true -> - [?Q, L, ?Q]; - false -> - json_encode_string_unicode(xmerl_ucs:from_utf8(L), State, [?Q]) - end; -json_encode_string(B, State) when is_binary(B) -> - case json_bin_is_safe(B) of - true -> - [?Q, B, ?Q]; - false -> - json_encode_string_unicode(xmerl_ucs:from_utf8(B), State, [?Q]) - end; -json_encode_string(I, _State) when is_integer(I) -> - [?Q, integer_to_list(I), ?Q]; -json_encode_string(L, State) when is_list(L) -> - case json_string_is_safe(L) of - true -> - [?Q, L, ?Q]; - false -> - json_encode_string_unicode(L, State, [?Q]) - end. - -json_string_is_safe([]) -> - true; -json_string_is_safe([C | Rest]) -> - case C of - ?Q -> - false; - $\\ -> - false; - $\b -> - false; - $\f -> - false; - $\n -> - false; - $\r -> - false; - $\t -> - false; - C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF -> - false; - C when C < 16#7f -> - json_string_is_safe(Rest); - _ -> - false - end. - -json_bin_is_safe(<<>>) -> - true; -json_bin_is_safe(<>) -> - case C of - ?Q -> - false; - $\\ -> - false; - $\b -> - false; - $\f -> - false; - $\n -> - false; - $\r -> - false; - $\t -> - false; - C when C >= 0, C < $\s; C >= 16#7f -> - false; - C when C < 16#7f -> - json_bin_is_safe(Rest) - end. - -json_encode_string_unicode([], _State, Acc) -> - lists:reverse([$\" | Acc]); -json_encode_string_unicode([C | Cs], State, Acc) -> - Acc1 = case C of - ?Q -> - [?Q, $\\ | Acc]; - %% Escaping solidus is only useful when trying to protect - %% against "" injection attacks which are only - %% possible when JSON is inserted into a HTML document - %% in-line. mochijson2 does not protect you from this, so - %% if you do insert directly into HTML then you need to - %% uncomment the following case or escape the output of encode. - %% - %% $/ -> - %% [$/, $\\ | Acc]; - %% - $\\ -> - [$\\, $\\ | Acc]; - $\b -> - [$b, $\\ | Acc]; - $\f -> - [$f, $\\ | Acc]; - $\n -> - [$n, $\\ | Acc]; - $\r -> - [$r, $\\ | Acc]; - $\t -> - [$t, $\\ | Acc]; - C when C >= 0, C < $\s -> - [unihex(C) | Acc]; - C when C >= 16#7f, C =< 16#10FFFF, State#encoder.utf8 -> - [xmerl_ucs:to_utf8(C) | Acc]; - C when C >= 16#7f, C =< 16#10FFFF, not State#encoder.utf8 -> - [unihex(C) | Acc]; - C when C < 16#7f -> - [C | Acc]; - _ -> - exit({json_encode, {bad_char, C}}) - end, - json_encode_string_unicode(Cs, State, Acc1). - -hexdigit(C) when C >= 0, C =< 9 -> - C + $0; -hexdigit(C) when C =< 15 -> - C + $a - 10. - -unihex(C) when C < 16#10000 -> - <> = <>, - Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]], - [$\\, $u | Digits]; -unihex(C) when C =< 16#10FFFF -> - N = C - 16#10000, - S1 = 16#d800 bor ((N bsr 10) band 16#3ff), - S2 = 16#dc00 bor (N band 16#3ff), - [unihex(S1), unihex(S2)]. - -json_decode(L, S) when is_list(L) -> - json_decode(iolist_to_binary(L), S); -json_decode(B, S) -> - {Res, S1} = decode1(B, S), - {eof, _} = tokenize(B, S1#decoder{state=trim}), - Res. - -decode1(B, S=#decoder{state=null}) -> - case tokenize(B, S#decoder{state=any}) of - {{const, C}, S1} -> - {C, S1}; - {start_array, S1} -> - decode_array(B, S1); - {start_object, S1} -> - decode_object(B, S1) - end. - -make_object(V, #decoder{object_hook=null}) -> - V; -make_object(V, #decoder{object_hook=Hook}) -> - Hook(V). - -decode_object(B, S) -> - decode_object(B, S#decoder{state=key}, []). - -decode_object(B, S=#decoder{state=key}, Acc) -> - case tokenize(B, S) of - {end_object, S1} -> - V = make_object({struct, lists:reverse(Acc)}, S1), - {V, S1#decoder{state=null}}; - {{const, K}, S1} -> - {colon, S2} = tokenize(B, S1), - {V, S3} = decode1(B, S2#decoder{state=null}), - decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc]) - end; -decode_object(B, S=#decoder{state=comma}, Acc) -> - case tokenize(B, S) of - {end_object, S1} -> - V = make_object({struct, lists:reverse(Acc)}, S1), - {V, S1#decoder{state=null}}; - {comma, S1} -> - decode_object(B, S1#decoder{state=key}, Acc) - end. - -decode_array(B, S) -> - decode_array(B, S#decoder{state=any}, []). - -decode_array(B, S=#decoder{state=any}, Acc) -> - case tokenize(B, S) of - {end_array, S1} -> - {lists:reverse(Acc), S1#decoder{state=null}}; - {start_array, S1} -> - {Array, S2} = decode_array(B, S1), - decode_array(B, S2#decoder{state=comma}, [Array | Acc]); - {start_object, S1} -> - {Array, S2} = decode_object(B, S1), - decode_array(B, S2#decoder{state=comma}, [Array | Acc]); - {{const, Const}, S1} -> - decode_array(B, S1#decoder{state=comma}, [Const | Acc]) - end; -decode_array(B, S=#decoder{state=comma}, Acc) -> - case tokenize(B, S) of - {end_array, S1} -> - {lists:reverse(Acc), S1#decoder{state=null}}; - {comma, S1} -> - decode_array(B, S1#decoder{state=any}, Acc) - end. - -tokenize_string(B, S=#decoder{offset=O}) -> - case tokenize_string_fast(B, O) of - {escape, O1} -> - Length = O1 - O, - S1 = ?ADV_COL(S, Length), - <<_:O/binary, Head:Length/binary, _/binary>> = B, - tokenize_string(B, S1, lists:reverse(binary_to_list(Head))); - O1 -> - Length = O1 - O, - <<_:O/binary, String:Length/binary, ?Q, _/binary>> = B, - {{const, String}, ?ADV_COL(S, Length + 1)} - end. - -tokenize_string_fast(B, O) -> - case B of - <<_:O/binary, ?Q, _/binary>> -> - O; - <<_:O/binary, $\\, _/binary>> -> - {escape, O}; - <<_:O/binary, C1, _/binary>> when C1 < 128 -> - tokenize_string_fast(B, 1 + O); - <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223, - C2 >= 128, C2 =< 191 -> - tokenize_string_fast(B, 2 + O); - <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239, - C2 >= 128, C2 =< 191, - C3 >= 128, C3 =< 191 -> - tokenize_string_fast(B, 3 + O); - <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244, - C2 >= 128, C2 =< 191, - C3 >= 128, C3 =< 191, - C4 >= 128, C4 =< 191 -> - tokenize_string_fast(B, 4 + O); - _ -> - throw(invalid_utf8) - end. - -tokenize_string(B, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, ?Q, _/binary>> -> - {{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)}; - <<_:O/binary, "\\\"", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]); - <<_:O/binary, "\\\\", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]); - <<_:O/binary, "\\/", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]); - <<_:O/binary, "\\b", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]); - <<_:O/binary, "\\f", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]); - <<_:O/binary, "\\n", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]); - <<_:O/binary, "\\r", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]); - <<_:O/binary, "\\t", _/binary>> -> - tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]); - <<_:O/binary, "\\u", C3, C2, C1, C0, Rest/binary>> -> - C = erlang:list_to_integer([C3, C2, C1, C0], 16), - if C > 16#D7FF, C < 16#DC00 -> - %% coalesce UTF-16 surrogate pair - <<"\\u", D3, D2, D1, D0, _/binary>> = Rest, - D = erlang:list_to_integer([D3,D2,D1,D0], 16), - [CodePoint] = xmerl_ucs:from_utf16be(<>), - Acc1 = lists:reverse(xmerl_ucs:to_utf8(CodePoint), Acc), - tokenize_string(B, ?ADV_COL(S, 12), Acc1); - true -> - Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc), - tokenize_string(B, ?ADV_COL(S, 6), Acc1) - end; - <<_:O/binary, C1, _/binary>> when C1 < 128 -> - tokenize_string(B, ?INC_CHAR(S, C1), [C1 | Acc]); - <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223, - C2 >= 128, C2 =< 191 -> - tokenize_string(B, ?ADV_COL(S, 2), [C2, C1 | Acc]); - <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239, - C2 >= 128, C2 =< 191, - C3 >= 128, C3 =< 191 -> - tokenize_string(B, ?ADV_COL(S, 3), [C3, C2, C1 | Acc]); - <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244, - C2 >= 128, C2 =< 191, - C3 >= 128, C3 =< 191, - C4 >= 128, C4 =< 191 -> - tokenize_string(B, ?ADV_COL(S, 4), [C4, C3, C2, C1 | Acc]); - _ -> - throw(invalid_utf8) - end. - -tokenize_number(B, S) -> - case tokenize_number(B, sign, S, []) of - {{int, Int}, S1} -> - {{const, list_to_integer(Int)}, S1}; - {{float, Float}, S1} -> - {{const, list_to_float(Float)}, S1} - end. - -tokenize_number(B, sign, S=#decoder{offset=O}, []) -> - case B of - <<_:O/binary, $-, _/binary>> -> - tokenize_number(B, int, ?INC_COL(S), [$-]); - _ -> - tokenize_number(B, int, S, []) - end; -tokenize_number(B, int, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, $0, _/binary>> -> - tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]); - <<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 -> - tokenize_number(B, int1, ?INC_COL(S), [C | Acc]) - end; -tokenize_number(B, int1, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> - tokenize_number(B, int1, ?INC_COL(S), [C | Acc]); - _ -> - tokenize_number(B, frac, S, Acc) - end; -tokenize_number(B, frac, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 -> - tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]); - <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> - tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]); - _ -> - {{int, lists:reverse(Acc)}, S} - end; -tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> - tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]); - <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> - tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]); - _ -> - {{float, lists:reverse(Acc)}, S} - end; -tokenize_number(B, esign, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ -> - tokenize_number(B, eint, ?INC_COL(S), [C | Acc]); - _ -> - tokenize_number(B, eint, S, Acc) - end; -tokenize_number(B, eint, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> - tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]) - end; -tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) -> - case B of - <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> - tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]); - _ -> - {{float, lists:reverse(Acc)}, S} - end. - -tokenize(B, S=#decoder{offset=O}) -> - case B of - <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> - tokenize(B, ?INC_CHAR(S, C)); - <<_:O/binary, "{", _/binary>> -> - {start_object, ?INC_COL(S)}; - <<_:O/binary, "}", _/binary>> -> - {end_object, ?INC_COL(S)}; - <<_:O/binary, "[", _/binary>> -> - {start_array, ?INC_COL(S)}; - <<_:O/binary, "]", _/binary>> -> - {end_array, ?INC_COL(S)}; - <<_:O/binary, ",", _/binary>> -> - {comma, ?INC_COL(S)}; - <<_:O/binary, ":", _/binary>> -> - {colon, ?INC_COL(S)}; - <<_:O/binary, "null", _/binary>> -> - {{const, null}, ?ADV_COL(S, 4)}; - <<_:O/binary, "true", _/binary>> -> - {{const, true}, ?ADV_COL(S, 4)}; - <<_:O/binary, "false", _/binary>> -> - {{const, false}, ?ADV_COL(S, 5)}; - <<_:O/binary, "\"", _/binary>> -> - tokenize_string(B, ?INC_COL(S)); - <<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9) - orelse C =:= $- -> - tokenize_number(B, S); - <<_:O/binary>> -> - trim = S#decoder.state, - {eof, S} - end. -%% -%% Tests -%% --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - - -%% testing constructs borrowed from the Yaws JSON implementation. - -%% Create an object from a list of Key/Value pairs. - -obj_new() -> - {struct, []}. - -is_obj({struct, Props}) -> - F = fun ({K, _}) when is_binary(K) -> true end, - lists:all(F, Props). - -obj_from_list(Props) -> - Obj = {struct, Props}, - ?assert(is_obj(Obj)), - Obj. - -%% Test for equivalence of Erlang terms. -%% Due to arbitrary order of construction, equivalent objects might -%% compare unequal as erlang terms, so we need to carefully recurse -%% through aggregates (tuples and objects). - -equiv({struct, Props1}, {struct, Props2}) -> - equiv_object(Props1, Props2); -equiv(L1, L2) when is_list(L1), is_list(L2) -> - equiv_list(L1, L2); -equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2; -equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2; -equiv(A, A) when A =:= true orelse A =:= false orelse A =:= null -> true. - -%% Object representation and traversal order is unknown. -%% Use the sledgehammer and sort property lists. - -equiv_object(Props1, Props2) -> - L1 = lists:keysort(1, Props1), - L2 = lists:keysort(1, Props2), - Pairs = lists:zip(L1, L2), - true = lists:all(fun({{K1, V1}, {K2, V2}}) -> - equiv(K1, K2) and equiv(V1, V2) - end, Pairs). - -%% Recursively compare tuple elements for equivalence. - -equiv_list([], []) -> - true; -equiv_list([V1 | L1], [V2 | L2]) -> - equiv(V1, V2) andalso equiv_list(L1, L2). - -decode_test() -> - [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>), - <<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]). - -e2j_vec_test() -> - test_one(e2j_test_vec(utf8), 1). - -test_one([], _N) -> - %% io:format("~p tests passed~n", [N-1]), - ok; -test_one([{E, J} | Rest], N) -> - %% io:format("[~p] ~p ~p~n", [N, E, J]), - true = equiv(E, decode(J)), - true = equiv(E, decode(encode(E))), - test_one(Rest, 1+N). - -e2j_test_vec(utf8) -> - [ - {1, "1"}, - {3.1416, "3.14160"}, %% text representation may truncate, trail zeroes - {-1, "-1"}, - {-3.1416, "-3.14160"}, - {12.0e10, "1.20000e+11"}, - {1.234E+10, "1.23400e+10"}, - {-1.234E-10, "-1.23400e-10"}, - {10.0, "1.0e+01"}, - {123.456, "1.23456E+2"}, - {10.0, "1e1"}, - {<<"foo">>, "\"foo\""}, - {<<"foo", 5, "bar">>, "\"foo\\u0005bar\""}, - {<<"">>, "\"\""}, - {<<"\n\n\n">>, "\"\\n\\n\\n\""}, - {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""}, - {obj_new(), "{}"}, - {obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"}, - {obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]), - "{\"foo\":\"bar\",\"baz\":123}"}, - {[], "[]"}, - {[[]], "[[]]"}, - {[1, <<"foo">>], "[1,\"foo\"]"}, - - %% json array in a json object - {obj_from_list([{<<"foo">>, [123]}]), - "{\"foo\":[123]}"}, - - %% json object in a json object - {obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]), - "{\"foo\":{\"bar\":true}}"}, - - %% fold evaluation order - {obj_from_list([{<<"foo">>, []}, - {<<"bar">>, obj_from_list([{<<"baz">>, true}])}, - {<<"alice">>, <<"bob">>}]), - "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"}, - - %% json object in a json array - {[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null], - "[-123,\"foo\",{\"bar\":[]},null]"} - ]. - -%% test utf8 encoding -encoder_utf8_test() -> - %% safe conversion case (default) - [34,"\\u0001","\\u0442","\\u0435","\\u0441","\\u0442",34] = - encode(<<1,"\321\202\320\265\321\201\321\202">>), - - %% raw utf8 output (optional) - Enc = mochijson2:encoder([{utf8, true}]), - [34,"\\u0001",[209,130],[208,181],[209,129],[209,130],34] = - Enc(<<1,"\321\202\320\265\321\201\321\202">>). - -input_validation_test() -> - Good = [ - {16#00A3, <>}, %% pound - {16#20AC, <>}, %% euro - {16#10196, <>} %% denarius - ], - lists:foreach(fun({CodePoint, UTF8}) -> - Expect = list_to_binary(xmerl_ucs:to_utf8(CodePoint)), - Expect = decode(UTF8) - end, Good), - - Bad = [ - %% 2nd, 3rd, or 4th byte of a multi-byte sequence w/o leading byte - <>, - %% missing continuations, last byte in each should be 80-BF - <>, - <>, - <>, - %% we don't support code points > 10FFFF per RFC 3629 - <>, - %% escape characters trigger a different code path - <> - ], - lists:foreach( - fun(X) -> - ok = try decode(X) catch invalid_utf8 -> ok end, - %% could be {ucs,{bad_utf8_character_code}} or - %% {json_encode,{bad_char,_}} - {'EXIT', _} = (catch encode(X)) - end, Bad). - -inline_json_test() -> - ?assertEqual(<<"\"iodata iodata\"">>, - iolist_to_binary( - encode({json, [<<"\"iodata">>, " iodata\""]}))), - ?assertEqual({struct, [{<<"key">>, <<"iodata iodata">>}]}, - decode( - encode({struct, - [{key, {json, [<<"\"iodata">>, " iodata\""]}}]}))), - ok. - -big_unicode_test() -> - UTF8Seq = list_to_binary(xmerl_ucs:to_utf8(16#0001d120)), - ?assertEqual( - <<"\"\\ud834\\udd20\"">>, - iolist_to_binary(encode(UTF8Seq))), - ?assertEqual( - UTF8Seq, - decode(iolist_to_binary(encode(UTF8Seq)))), - ok. - -custom_decoder_test() -> - ?assertEqual( - {struct, [{<<"key">>, <<"value">>}]}, - (decoder([]))("{\"key\": \"value\"}")), - F = fun ({struct, [{<<"key">>, <<"value">>}]}) -> win end, - ?assertEqual( - win, - (decoder([{object_hook, F}]))("{\"key\": \"value\"}")), - ok. - -atom_test() -> - %% JSON native atoms - [begin - ?assertEqual(A, decode(atom_to_list(A))), - ?assertEqual(iolist_to_binary(atom_to_list(A)), - iolist_to_binary(encode(A))) - end || A <- [true, false, null]], - %% Atom to string - ?assertEqual( - <<"\"foo\"">>, - iolist_to_binary(encode(foo))), - ?assertEqual( - <<"\"\\ud834\\udd20\"">>, - iolist_to_binary(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))), - ok. - -key_encode_test() -> - %% Some forms are accepted as keys that would not be strings in other - %% cases - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode({struct, [{foo, 1}]}))), - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode({struct, [{<<"foo">>, 1}]}))), - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode({struct, [{"foo", 1}]}))), - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode([{foo, 1}]))), - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode([{<<"foo">>, 1}]))), - ?assertEqual( - <<"{\"foo\":1}">>, - iolist_to_binary(encode([{"foo", 1}]))), - ?assertEqual( - <<"{\"\\ud834\\udd20\":1}">>, - iolist_to_binary( - encode({struct, [{[16#0001d120], 1}]}))), - ?assertEqual( - <<"{\"1\":1}">>, - iolist_to_binary(encode({struct, [{1, 1}]}))), - ok. - -unsafe_chars_test() -> - Chars = "\"\\\b\f\n\r\t", - [begin - ?assertEqual(false, json_string_is_safe([C])), - ?assertEqual(false, json_bin_is_safe(<>)), - ?assertEqual(<>, decode(encode(<>))) - end || C <- Chars], - ?assertEqual( - false, - json_string_is_safe([16#0001d120])), - ?assertEqual( - false, - json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8(16#0001d120)))), - ?assertEqual( - [16#0001d120], - xmerl_ucs:from_utf8( - binary_to_list( - decode(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))))), - ?assertEqual( - false, - json_string_is_safe([16#110000])), - ?assertEqual( - false, - json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8([16#110000])))), - %% solidus can be escaped but isn't unsafe by default - ?assertEqual( - <<"/">>, - decode(<<"\"\\/\"">>)), - ok. - -int_test() -> - ?assertEqual(0, decode("0")), - ?assertEqual(1, decode("1")), - ?assertEqual(11, decode("11")), - ok. - -large_int_test() -> - ?assertEqual(<<"-2147483649214748364921474836492147483649">>, - iolist_to_binary(encode(-2147483649214748364921474836492147483649))), - ?assertEqual(<<"2147483649214748364921474836492147483649">>, - iolist_to_binary(encode(2147483649214748364921474836492147483649))), - ok. - -float_test() -> - ?assertEqual(<<"-2147483649.0">>, iolist_to_binary(encode(-2147483649.0))), - ?assertEqual(<<"2147483648.0">>, iolist_to_binary(encode(2147483648.0))), - ok. - -handler_test() -> - ?assertEqual( - {'EXIT',{json_encode,{bad_term,{}}}}, - catch encode({})), - F = fun ({}) -> [] end, - ?assertEqual( - <<"[]">>, - iolist_to_binary((encoder([{handler, F}]))({}))), - ok. - --endif. From e957c810e94aa610dc04a68cb5251b6ccb5a1726 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 13:21:16 +0100 Subject: [PATCH 010/405] Ignore log directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 08599f4..928d8bf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ apps/*/ebin/*.beam apps/*/ebin/*.app deps/ *.swp +log/ From ec698411b3c6271a9e02dbae769b11cbbbfae84e Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Sat, 9 Jul 2011 13:22:15 +0100 Subject: [PATCH 011/405] added kludged version of etop --- apps/bigwig/include/etop.hrl | 20 ++ apps/bigwig/include/etop_defs.hrl | 29 ++ apps/bigwig/include/observer_backend.hrl | 42 +++ apps/bigwig/src/etop2.erl | 348 +++++++++++++++++++++++ apps/bigwig/src/etop2_json.erl | 103 +++++++ 5 files changed, 542 insertions(+) create mode 100644 apps/bigwig/include/etop.hrl create mode 100644 apps/bigwig/include/etop_defs.hrl create mode 100644 apps/bigwig/include/observer_backend.hrl create mode 100644 apps/bigwig/src/etop2.erl create mode 100644 apps/bigwig/src/etop2_json.erl diff --git a/apps/bigwig/include/etop.hrl b/apps/bigwig/include/etop.hrl new file mode 100644 index 0000000..5a8e665 --- /dev/null +++ b/apps/bigwig/include/etop.hrl @@ -0,0 +1,20 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-include("observer_backend.hrl"). diff --git a/apps/bigwig/include/etop_defs.hrl b/apps/bigwig/include/etop_defs.hrl new file mode 100644 index 0000000..84b44e2 --- /dev/null +++ b/apps/bigwig/include/etop_defs.hrl @@ -0,0 +1,29 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-define(SYSFORM, + " ~-72w~10s~n" + " Load: cpu ~8w Memory: total ~8w binary ~8w~n" + " procs~8w processes~8w code ~8w~n" + " runq ~8w atom ~8w ets ~8w~n"). + +-record(opts, {node=node(), port = 8415, accum = false, intv = 5000, lines = 10, + width = 700, height = 340, sort = runtime, tracing = on, + %% Other state information + out_mod=etop2_json, out_proc, server, host, tracer, store, + accum_tab, remote}). diff --git a/apps/bigwig/include/observer_backend.hrl b/apps/bigwig/include/observer_backend.hrl new file mode 100644 index 0000000..4be9bac --- /dev/null +++ b/apps/bigwig/include/observer_backend.hrl @@ -0,0 +1,42 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-record(etop_info, + {now = {0, 0, 0}, + n_procs = 0, + wall_clock = {0, 0}, + runtime = {0, 0}, + run_queue = 0, + alloc_areas = [], + memi = [{total, 0}, + {processes, 0}, + {ets, 0}, + {atom, 0}, + {code, 0}, + {binary, 0}], + procinfo = [] + }). + +-record(etop_proc_info, + {pid, + mem=0, + reds=0, + name, + runtime=0, + cf, + mq=0}). diff --git a/apps/bigwig/src/etop2.erl b/apps/bigwig/src/etop2.erl new file mode 100644 index 0000000..0cc16fe --- /dev/null +++ b/apps/bigwig/src/etop2.erl @@ -0,0 +1,348 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(etop2). +-author('siri@erix.ericsson.se'). + +-behaviour(gen_server). + +-export([start/0, start/1, config/2, stop/0, dump/1, help/0]). +%% Internal +-export([update/1]). +-export([loadinfo/1, meminfo/2, getopt/2]). + +-include("etop.hrl"). +-include("etop_defs.hrl"). + +-define(change_at_runtime_config,[lines,interval,sort,accumulate]). + +-define(SERVER, etop2_server). + +help() -> + io:format( + "Usage of the erlang top program~n" + "Options are set as command line parameters as in -node a@host -..~n" + "or as parameter to etop:start([{node, a@host}, {...}])~n" + "Options are:~n" + " node atom Required The erlang node to measure ~n" + " port integer The used port, NOTE: due to a bug this program~n" + " will hang if the port is not avaiable~n" + " accumulate boolean If true execution time is accumulated ~n" + " lines integer Number of displayed processes~n" + " interval integer Display update interval in secs~n" + " sort runtime | reductions | memory | msg_q~n" + " What information to sort by~n" + " Default: runtime (reductions if tracing=off)~n" + " output graphical | text~n" + " How to present results~n" + " Default: graphical~n" + " tracing on | off etop uses the erlang trace facility, and thus~n" + " no other tracing is possible on the node while~n" + " etop is running, unless this option is set to~n" + " 'off'. Also helpful if the etop tracing causes~n" + " too high load on the measured node.~n" + " With tracing off, runtime is not measured!~n" + " setcookie string Only applicable on operating system command~n" + " line. Set cookie for the etop node, must be~n" + " same as the cookie for the measured node.~n" + " This is not an etop parameter~n" + ). + +stop() -> + case whereis(?SERVER) of + undefined -> not_started; + Pid when is_pid(Pid) -> ?SERVER ! stop + end. + +config(Key,Value) -> + case check_runtime_config(Key,Value) of + ok -> + ?SERVER ! {config,{Key,Value}}, + ok; + error -> + {error,illegal_opt} + end. +check_runtime_config(lines,L) when is_integer(L),L>0 -> ok; +check_runtime_config(interval,I) when is_integer(I),I>0 -> ok; +check_runtime_config(sort,S) when S=:=runtime; + S=:=reductions; + S=:=memory; + S=:=msg_q -> ok; +check_runtime_config(accumulate,A) when A=:=true; A=:=false -> ok; +check_runtime_config(_Key,_Value) -> error. + +dump(File) -> + case file:open(File,[write]) of + {ok,Fd} -> ?SERVER ! {dump,Fd}; + Error -> Error + end. + +start() -> + start([]). + +start(Opts) -> + process_flag(trap_exit, true), + Config1 = handle_args(init:get_arguments() ++ Opts, #opts{}), + Config2 = Config1#opts{server=self()}, + + %% Connect to the node we want to look at + Node = getopt(node, Config2), + case net_adm:ping(Node) of + pang when Node /= node() -> + io:format("Error Couldn't connect to node ~p ~n~n", [Node]), + help(), + exit("connection error"); + _pong -> + check_runtime_tools_vsn(Node) + end, + + %% Maybe set up the tracing + Config3 = + if Config2#opts.tracing == on, Node /= node() -> + %% Cannot trace on current node since the tracer will + %% trace itself + etop_tr:setup_tracer(Config2); + true -> + if Config2#opts.sort == runtime -> + Config2#opts{sort=reductions,tracing=off}; + true -> + Config2#opts{tracing=off} + end + end, + AccumTab = ets:new(accum_tab, + [set,public,{keypos,#etop_proc_info.pid}]), + Config4 = Config3#opts{accum_tab=AccumTab}, + + %% Start the output server + Out = spawn_link(Config4#opts.out_mod, init, [Config4]), + Config5 = Config4#opts{out_proc = Out}, + + init_data_handler(Config5), + ok. + +check_runtime_tools_vsn(Node) -> + case rpc:call(Node,observer_backend,vsn,[]) of + {ok,Vsn} -> check_vsn(Vsn); + _ -> exit("Faulty version of runtime_tools on remote node") + end. +check_vsn(_Vsn) -> ok. +%check_vsn(_Vsn) -> exit("Faulty version of runtime_tools on remote node"). + + +%% Handle the incoming data + +init_data_handler(Config) -> + register(?SERVER,self()), + Reader = + if Config#opts.tracing == on -> etop_tr:reader(Config); + true -> undefined + end, + data_handler(Reader, Config). + +data_handler(Reader, Opts) -> + receive + stop -> + stop(Opts), + ok; + {config,{Key,Value}} -> + data_handler(Reader,putopt(Key,Value,Opts)); + {dump,Fd} -> + Opts#opts.out_proc ! {dump,Fd}, + data_handler(Reader,Opts); + {'EXIT', EPid, Reason} when EPid == Opts#opts.out_proc -> + case Reason of + normal -> ok; + _ -> io:format("Output server crashed: ~p~n",[Reason]) + end, + stop(Opts), + out_proc_stopped; + {'EXIT', Reader, eof} -> + io:format("Lost connection to node ~p exiting~n", [Opts#opts.node]), + stop(Opts), + connection_lost; + _ -> + data_handler(Reader, Opts) + end. + +stop(Opts) -> + (Opts#opts.out_mod):stop(Opts#opts.out_proc), + if Opts#opts.tracing == on -> etop_tr:stop_tracer(Opts); + true -> ok + end, + unregister(?SERVER). + +update(#opts{store=Store,node=Node,tracing=Tracing}=Opts) -> + Pid = spawn_link(Node,observer_backend,etop_collect,[self()]), + Info = receive {Pid,I} -> I + after 1000 -> exit(connection_lost) + end, + #etop_info{procinfo=ProcInfo} = Info, + ProcInfo1 = + if Tracing == on -> + PI=lists:map(fun(PI=#etop_proc_info{pid=P}) -> + case ets:lookup(Store,P) of + [{P,T}] -> PI#etop_proc_info{runtime=T}; + [] -> PI + end + end, + ProcInfo), + PI; + true -> + lists:map(fun(PI) -> PI#etop_proc_info{runtime='-'} end,ProcInfo) + end, + ProcInfo2 = sort(Opts,ProcInfo1), + Info#etop_info{procinfo=ProcInfo2}. + +sort(Opts,PI) -> + Tag = get_tag(Opts#opts.sort), + PI1 = if Opts#opts.accum -> + PI; + true -> + AccumTab = Opts#opts.accum_tab, + lists:map( + fun(#etop_proc_info{pid=Pid,reds=Reds,runtime=RT}=I) -> + NewI = + case ets:lookup(AccumTab,Pid) of + [#etop_proc_info{reds=OldReds, + runtime='-'}] -> + I#etop_proc_info{reds=Reds-OldReds, + runtime='-'}; + [#etop_proc_info{reds=OldReds, + runtime=OldRT}] -> + I#etop_proc_info{reds=Reds-OldReds, + runtime=RT-OldRT}; + [] -> + I + end, + ets:insert(AccumTab,I), + NewI + end, + PI) + end, + PI2 = lists:reverse(lists:keysort(Tag,PI1)), + lists:sublist(PI2,Opts#opts.lines). + +get_tag(runtime) -> #etop_proc_info.runtime; +get_tag(memory) -> #etop_proc_info.mem; +get_tag(reductions) -> #etop_proc_info.reds; +get_tag(msg_q) -> #etop_proc_info.mq. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Configuration Management + +getopt(What, Config) when is_record(Config, opts) -> + case What of + node -> Config#opts.node; + port -> Config#opts.port; + accum -> Config#opts.accum; + intv -> Config#opts.intv; + lines -> Config#opts.lines; + sort -> Config#opts.sort; + width -> Config#opts.width; + height-> Config#opts.height; + + store -> Config#opts.store; + host -> Config#opts.host + end. + +putopt(Key, Value, Config) when is_record(Config, opts) -> + Config1 = handle_args([{Key,Value}],Config), + Config1#opts.out_proc ! {config,{Key,Value},Config1}, + Config1. + +handle_args([{node, [NodeString]}| R], Config) when is_list(NodeString) -> + Node = list_to_atom(NodeString), + NewC = Config#opts{node = Node}, + handle_args(R, NewC); +handle_args([{node, Node} |R], Config) when is_atom(Node) -> + NewC = Config#opts{node = Node}, + handle_args(R, NewC); +handle_args([{port, Port}| R], Config) when is_integer(Port) -> + NewC = Config#opts{port=Port}, + handle_args(R, NewC); +handle_args([{port, [Port]}| R], Config) when is_list(Port) -> + NewC = Config#opts{port= list_to_integer(Port)}, + handle_args(R, NewC); +handle_args([{interval, Time}| R], Config) when is_integer(Time)-> + NewC = Config#opts{intv=Time*1000}, + handle_args(R, NewC); +handle_args([{interval, [Time]}| R], Config) when is_list(Time)-> + NewC = Config#opts{intv=list_to_integer(Time)*1000}, + handle_args(R, NewC); +handle_args([{lines, Lines}| R], Config) when is_integer(Lines) -> + NewC = Config#opts{lines=Lines}, + handle_args(R, NewC); +handle_args([{lines, [Lines]}| R], Config) when is_list(Lines) -> + NewC = Config#opts{lines= list_to_integer(Lines)}, + handle_args(R, NewC); +handle_args([{accumulate, Bool}| R], Config) when is_atom(Bool) -> + NewC = Config#opts{accum=Bool}, + handle_args(R, NewC); +handle_args([{accumulate, [Bool]}| R], Config) when is_list(Bool) -> + NewC = Config#opts{accum= list_to_atom(Bool)}, + handle_args(R, NewC); +handle_args([{sort, Sort}| R], Config) when is_atom(Sort) -> + NewC = Config#opts{sort=Sort}, + handle_args(R, NewC); +handle_args([{sort, [Sort]}| R], Config) when is_list(Sort) -> + NewC = Config#opts{sort= list_to_atom(Sort)}, + handle_args(R, NewC); +handle_args([{output, Output}| R], Config) when is_atom(Output) -> + NewC = Config#opts{out_mod=output(Output)}, + handle_args(R, NewC); +handle_args([{output, [Output]}| R], Config) when is_list(Output) -> + NewC = Config#opts{out_mod= output(list_to_atom(Output))}, + handle_args(R, NewC); +handle_args([{tracing, OnOff}| R], Config) when is_atom(OnOff) -> + NewC = Config#opts{tracing=OnOff}, + handle_args(R, NewC); +handle_args([{tracing, [OnOff]}| R], Config) when is_list(OnOff) -> + NewC = Config#opts{tracing=list_to_atom(OnOff)}, + handle_args(R, NewC); + +handle_args([_| R], C) -> + handle_args(R, C); +handle_args([], C) -> + C. + +output(graphical) -> etop_gui; +output(text) -> etop_txt; +output(json) -> etop_json. + + +loadinfo(SysI) -> + #etop_info{n_procs = Procs, + run_queue = RQ, + now = Now, + wall_clock = {_, WC}, + runtime = {_, RT}} = SysI, + Cpu = round(100*RT/WC), + Clock = io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w", + tuple_to_list(element(2,calendar:now_to_datetime(Now)))), + {Cpu,Procs,RQ,Clock}. + +meminfo(MemI, [Tag|Tags]) -> + [round(get_mem(Tag, MemI)/1024)|meminfo(MemI, Tags)]; +meminfo(_MemI, []) -> []. + +get_mem(Tag, MemI) -> + case lists:keysearch(Tag, 1, MemI) of + {value, {Tag, I}} -> I; %these are in bytes + _ -> 0 + end. diff --git a/apps/bigwig/src/etop2_json.erl b/apps/bigwig/src/etop2_json.erl new file mode 100644 index 0000000..a8f3294 --- /dev/null +++ b/apps/bigwig/src/etop2_json.erl @@ -0,0 +1,103 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(etop2_json). +-author('siri@erix.ericsson.se'). + +-export([init/1,stop/1]). +-export([do_update/3]). + +-include_lib("etop.hrl"). +-include("etop_defs.hrl"). + +stop(Pid) -> Pid ! stop. + +init(Config) -> + loop(Config). + +loop(Config) -> + Info = do_update(Config), + receive + stop -> stopped; + {dump,Fd} -> do_update(Fd,Info,Config), loop(Config); + {config,_,Config1} -> loop(Config1) + after Config#opts.intv -> loop(Config) + end. + +do_update(Config) -> + Info = etop:update(Config), + do_update(standard_io,Info,Config). + +do_update(Fd,Info,Config) -> + {Cpu,NProcs,RQ,Clock} = etop2:loadinfo(Info), + Header = + case Info#etop_info.memi of + undefined -> + H = + [{<<"node">>, Config#opts.node}, + {<<"clock">>, iolist_to_binary(Clock)}, + {<<"cpu">>, Cpu}, + {<<"nprocs">>, NProcs}, + {<<"runqueue">>, RQ}], + {<<"header">>, H}; + Memi -> + [Tot,Procs,Atom,Bin,Code,Ets] = + etop2:meminfo(Memi, [total,processes,atom,binary,code,ets]), + H = + [{<<"node">>, Config#opts.node}, + {<<"clock">>, iolist_to_binary(Clock)}, + {<<"cpu">>, Cpu}, + {<<"tot">>, Tot}, + {<<"bin">>, Bin}, + {<<"nprocs">>, NProcs}, + {<<"procs">>, Procs}, + {<<"code">>, Code}, + {<<"runqueue">>, RQ}, + {<<"atom">>, Atom}, + {<<"ets">>, Ets}], + {<<"header">>, H} + end, + Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], + io:fwrite(Fd, "~p", [Header]), + io:fwrite(Fd, "~p", [Ps]), + Info. + +formatmfa({M, F, A}) -> + io_lib:format("~w:~w/~w",[M, F, A]). + +to_list(Name) when is_atom(Name) -> atom_to_list(Name); +to_list({_M,_F,_A}=MFA) -> formatmfa(MFA). + +etop_proc_info_to_json( + #etop_proc_info{pid=Pid, + mem=Mem, + reds=Reds, + name=Name, + runtime=Time, + cf=MFA, + mq=MQ}) -> + PInfo = + [{<<"pid">>, Pid}, + {<<"name">>, to_list(Name)}, + {<<"time">>, Time}, + {<<"reds">>, Reds}, + {<<"mem">>, Mem}, + {<<"mq">>, MQ}, + {<<"mfa">>, formatmfa(MFA)}], + {<<"pinfo">>, PInfo}. From 38ab31b0eb0d37b015462b3c3d7e701184c62e69 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 13:36:25 +0100 Subject: [PATCH 012/405] Return rb2 reports as JSON --- apps/bigwig/src/bigwig_http_rb.erl | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/apps/bigwig/src/bigwig_http_rb.erl b/apps/bigwig/src/bigwig_http_rb.erl index 28bc8c8..8bd75ee 100644 --- a/apps/bigwig/src/bigwig_http_rb.erl +++ b/apps/bigwig/src/bigwig_http_rb.erl @@ -25,27 +25,8 @@ handle_path([<<"rb">>, <<"reports">>], Req, State) -> handle_path([<<"rb">>, <<"reports">>, IdBin], Req, State) -> Id = list_to_integer(binary_to_list(IdBin)), Rep = rb2:load_number(Id), - %% TODO convert this to JSON: - %% {ok,"2011-07-09 11:42:37", - %% [{report_level,info_report}, - %% {group_leader,<0.36.0>}, - %% {date,{{2011,7,9},{11,42,37}}}, - %% {pid,<0.39.0>}, - %% {report_type,progress}, - %% {data,[{supervisor,{local,sasl_sup}}, - %% {started,[{pid,<0.170.0>}, - %% {name,rb2_server}, - %% {mfargs,{rb2,start_link,[[]]}}, - %% {restart_type,temporary}, - %% {shutdown,brutal_kill}, - %% {child_type,worker}]}]}]} - %% - - %% TODO Report is the term that needs JSONifying - %% {ok, Date, Report, ReportStr} = Report, - Body = io_lib:format("~p",[Rep]), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_http_req:reply(200, Headers, report_to_json(Rep), Req), {ok, Req2, State}; handle_path(Path, Req, State) -> @@ -57,6 +38,11 @@ terminate(_Req, _State) -> ok. +report_to_json({_, {ok, Date0, Report, ReportStr}}) -> + Date = list_to_binary(Date0), + jsx:term_to_json([{date, Date}, {report, Report}, {report_str, ReportStr}]). + + list_reports() -> lists:map( fun({Id,Type,Pid,Date}) -> {list_to_binary(integer_to_list(Id)), From 4735083742a9546e3cd4067160e193a96c0285a2 Mon Sep 17 00:00:00 2001 From: Steven Gravell Date: Sat, 9 Jul 2011 13:38:53 +0100 Subject: [PATCH 013/405] basic app list --- apps/bigwig/priv/css/body.css | 9 +++++ apps/bigwig/priv/html/index.html | 7 ++++ apps/bigwig/priv/js/jquery-1.6.2.min.js | 18 ++++++++++ apps/bigwig/priv/js/tpl.js | 38 +++++++++++++++++++++ apps/bigwig/src/bigwig_http_wh.erl | 44 ++++++++++++++++++------- 5 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 apps/bigwig/priv/css/body.css create mode 100644 apps/bigwig/priv/js/jquery-1.6.2.min.js create mode 100644 apps/bigwig/priv/js/tpl.js diff --git a/apps/bigwig/priv/css/body.css b/apps/bigwig/priv/css/body.css new file mode 100644 index 0000000..817b37a --- /dev/null +++ b/apps/bigwig/priv/css/body.css @@ -0,0 +1,9 @@ +body { + font-family: Consolas, "Droid Sans Mono", Monaco, Courier, monospace; + font-size: 12px; + letter-spacing: 1px; +} +._tpl { + display: none; + visibility: none; +} diff --git a/apps/bigwig/priv/html/index.html b/apps/bigwig/priv/html/index.html index 6a470c5..6dc416f 100644 --- a/apps/bigwig/priv/html/index.html +++ b/apps/bigwig/priv/html/index.html @@ -1,9 +1,16 @@ So Peter... what's happening? + +

+
    +
  • +
+ + diff --git a/apps/bigwig/priv/js/jquery-1.6.2.min.js b/apps/bigwig/priv/js/jquery-1.6.2.min.js new file mode 100644 index 0000000..48590ec --- /dev/null +++ b/apps/bigwig/priv/js/jquery-1.6.2.min.js @@ -0,0 +1,18 @@ +/*! + * jQuery JavaScript Library v1.6.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Jun 30 14:16:56 2011 -0400 + */ +(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"":"")+""),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i. +shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j +)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/)<[^<]*)*<\/script>/gi,bM=/^(?:select|textarea)/i,bN=/\s+/,bO=/([?&])_=[^&]*/,bP=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bQ=f.fn.load,bR={},bS={},bT,bU;try{bT=e.href}catch(bV){bT=c.createElement("a"),bT.href="",bT=bT.href}bU=bP.exec(bT.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bQ)return bQ.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bL,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bM.test(this.nodeName)||bG.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bD,"\r\n")}}):{name:b.name,value:c.replace(bD,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bT,isLocal:bH.test(bU[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bW(bR),ajaxTransport:bW(bS),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?bZ(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b$(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bF.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bE,"").replace(bJ,bU[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bN),d.crossDomain==null&&(r=bP.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bU[1]&&r[2]==bU[2]&&(r[3]||(r[1]==="http:"?80:443))==(bU[3]||(bU[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bX(bR,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bI.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bK.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bO,"$1_="+x);d.url=y+(y===d.url?(bK.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bX(bS,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bB,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn,co=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cr("show",3),a,b,c);for(var g=0,h=this.length;g=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b
";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cu.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cu.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cv(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cv(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/apps/bigwig/priv/js/tpl.js b/apps/bigwig/priv/js/tpl.js new file mode 100644 index 0000000..b82ec07 --- /dev/null +++ b/apps/bigwig/priv/js/tpl.js @@ -0,0 +1,38 @@ + + +tpl = (function() { + var title = function(title) { + $('#title').text(title); + } + title("Loading..."); + + var li = function(p, el) { + var id = el.id + var li = $('li[data-id='+id+']', p); + if(li.length==0) { + li = $('li._tpl',p).clone(false, false); + li.removeClass('_tpl'); + p.append(li); + } + li.text(id); + } + + var sock = new WebSocket("ws://10.100.1.188:8080/wh"); + sock.onopen = function() { + + } + sock.onmessage = function(msg) { + var json = $.parseJSON(msg.data); + console.log(json); + title(json.title); + var apps = $('#apps'); + for(var i=0; i - {ok, Req, undefined_state}. +init({tcp, http}, _Req, _Opts) -> + {upgrade, protocol, cowboy_http_websocket}. -handle(Req, State) -> - Headers = [{<<"Content-Type">>, <<"text/html">>}], - % todo: read file into state on init - {ok, Body} = tpl("index"), - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), - {ok, Req2, State}. +handle(_Req, _State) -> + error(websockets_only). terminate(_Req, _State) -> + error(websockets_only). + +websocket_init(_TransportName, Req, _Opts) -> + send({struct, [ + {title, <<"So Peter... What's Happening?">>}, + apps() + ]}), + {ok, Req, undefined_state}. + +websocket_handle({timeout, _Ref, Msg}, Req, State) -> + {reply, Msg, Req, State}; + +websocket_handle({websocket, Msg}, Req, State) -> + {reply, <<"Hello ", Msg/binary>>, Req, State}. + +websocket_terminate(_Reason, _Req, _State) -> ok. -tpl(Name) -> - file:read_file(tpl_path(Name)). +send(T) when is_tuple(T) -> + send(iolist_to_binary(mochijson2:encode(T))); + +send(Bin) -> + erlang:start_timer(0, self(), Bin). -tpl_path(Name) -> - filename:join([code:priv_dir(bigwig), "html", Name ++ ".html"]). +apps() -> + {apps, [ + {struct, [{id, App}]} + || {App, _Desc, _Vsn} <- application:which_applications() + ]}. From 85e4e209235e1865dae6cea5f3f1a49a8c7c0bfb Mon Sep 17 00:00:00 2001 From: Steven Gravell Date: Sat, 9 Jul 2011 14:17:48 +0100 Subject: [PATCH 014/405] add index page with static handler --- apps/bigwig/priv/js/tpl.js | 4 ++-- apps/bigwig/src/bigwig_http.erl | 3 ++- apps/bigwig/src/bigwig_http_static.erl | 16 ++++++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/apps/bigwig/priv/js/tpl.js b/apps/bigwig/priv/js/tpl.js index b82ec07..3ab24d7 100644 --- a/apps/bigwig/priv/js/tpl.js +++ b/apps/bigwig/priv/js/tpl.js @@ -16,8 +16,8 @@ tpl = (function() { } li.text(id); } - - var sock = new WebSocket("ws://10.100.1.188:8080/wh"); + var host = document.location.host; + var sock = new WebSocket("ws://"+host+"/wh/ws"); sock.onopen = function() { } diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl index f45d1da..7ad26d0 100644 --- a/apps/bigwig/src/bigwig_http.erl +++ b/apps/bigwig/src/bigwig_http.erl @@ -26,7 +26,8 @@ dispatch_rules() -> {[<<"">>], bigwig_http_index, []} , {[<<"static">>, '...'], bigwig_http_static, []} - , {[<<"wh">>, '...'], bigwig_http_wh, []} + , {[<<"wh">>], bigwig_http_static, ["html","index.html"]} + , {[<<"wh">>, <<"ws">>], bigwig_http_wh, []} , {[<<"vm">>], bigwig_http_vm, []} , {[<<"rb">>, '...'], bigwig_http_rb, []} diff --git a/apps/bigwig/src/bigwig_http_static.erl b/apps/bigwig/src/bigwig_http_static.erl index 6dd6c2e..27de59e 100644 --- a/apps/bigwig/src/bigwig_http_static.erl +++ b/apps/bigwig/src/bigwig_http_static.erl @@ -9,12 +9,20 @@ -compile(export_all). -init({tcp, http}, Req, _Opts) -> - {ok, Req, undefined_state}. +init({tcp, http}, Req, []) -> + {ok, Req, undefined_state}; +init({tcp, http}, Req, OnlyFile) -> + {ok, Req, OnlyFile}. -handle(Req, State) -> - Headers = [{<<"Content-Type">>, <<"text/html">>}], +handle(Req, undefined_state = State) -> {[_|Path], _} = cowboy_http_req:path(Req), % strip <<"static">> + send(Req, Path, State); + +handle(Req, OnlyFile = State) -> + send(Req, OnlyFile, State). + +send(Req, Path, State) -> + Headers = [{<<"Content-Type">>, <<"text/html">>}], {ok, Body} = file(filename:join(Path)), {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), {ok, Req2, State}. From e7aa3b5290ca12a4b617137291a1cc8283fcb7d3 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Sat, 9 Jul 2011 14:30:44 +0100 Subject: [PATCH 015/405] slowly converting to OTP --- apps/bigwig/include/etop_defs.hrl | 2 +- apps/bigwig/src/etop2.erl | 444 +++++++++++++++--------------- apps/bigwig/src/etop2_json.erl | 103 ------- 3 files changed, 230 insertions(+), 319 deletions(-) delete mode 100644 apps/bigwig/src/etop2_json.erl diff --git a/apps/bigwig/include/etop_defs.hrl b/apps/bigwig/include/etop_defs.hrl index 84b44e2..4bb83b9 100644 --- a/apps/bigwig/include/etop_defs.hrl +++ b/apps/bigwig/include/etop_defs.hrl @@ -25,5 +25,5 @@ -record(opts, {node=node(), port = 8415, accum = false, intv = 5000, lines = 10, width = 700, height = 340, sort = runtime, tracing = on, %% Other state information - out_mod=etop2_json, out_proc, server, host, tracer, store, + server, host, tracer, store, accum_tab, remote}). diff --git a/apps/bigwig/src/etop2.erl b/apps/bigwig/src/etop2.erl index 0cc16fe..2f488b1 100644 --- a/apps/bigwig/src/etop2.erl +++ b/apps/bigwig/src/etop2.erl @@ -21,7 +21,13 @@ -behaviour(gen_server). --export([start/0, start/1, config/2, stop/0, dump/1, help/0]). +%% API +-export([start_link/0, start_link/1, update/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). + +-export([config/2, help/0]). %% Internal -export([update/1]). -export([loadinfo/1, meminfo/2, getopt/2]). @@ -63,11 +69,11 @@ help() -> " This is not an etop parameter~n" ). -stop() -> - case whereis(?SERVER) of - undefined -> not_started; - Pid when is_pid(Pid) -> ?SERVER ! stop - end. +terminate(_Reason, State) -> + %% TODO: port over tracer? + if State#opts.tracing == on -> etop_tr:stop_tracer(State); + true -> ok + end. config(Key,Value) -> case check_runtime_config(Key,Value) of @@ -86,156 +92,123 @@ check_runtime_config(sort,S) when S=:=runtime; check_runtime_config(accumulate,A) when A=:=true; A=:=false -> ok; check_runtime_config(_Key,_Value) -> error. -dump(File) -> - case file:open(File,[write]) of - {ok,Fd} -> ?SERVER ! {dump,Fd}; - Error -> Error - end. - -start() -> - start([]). - -start(Opts) -> - process_flag(trap_exit, true), - Config1 = handle_args(init:get_arguments() ++ Opts, #opts{}), - Config2 = Config1#opts{server=self()}, - - %% Connect to the node we want to look at - Node = getopt(node, Config2), - case net_adm:ping(Node) of - pang when Node /= node() -> - io:format("Error Couldn't connect to node ~p ~n~n", [Node]), - help(), - exit("connection error"); - _pong -> - check_runtime_tools_vsn(Node) +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +start_link(Opts) -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [Opts], []). + +handle_cast(_Msg, State) -> + {stop, unhandled_cast, State}. +handle_info(_Info, State) -> + {stop, unhandled_info, State}. +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +init(Opts) -> + Config1 = handle_args(init:get_arguments() ++ Opts, #opts{}), + Config2 = Config1#opts{server=self()}, + + %% Connect to the node we want to look at + Node = getopt(node, Config2), + case net_adm:ping(Node) of + pang when Node /= node() -> + io:format("Error Couldn't connect to node ~p ~n~n", [Node]), + help(), + exit("connection error"); + _pong -> + check_runtime_tools_vsn(Node) + end, + + %% Maybe set up the tracing + Config3 = + if Config2#opts.tracing == on, Node /= node() -> + %% Cannot trace on current node since the tracer will + %% trace itself + etop_tr:setup_tracer(Config2); + true -> + if Config2#opts.sort == runtime -> + Config2#opts{sort=reductions,tracing=off}; + true -> + Config2#opts{tracing=off} + end end, + AccumTab = ets:new(accum_tab, + [set,public,{keypos,#etop_proc_info.pid}]), + Config4 = Config3#opts{accum_tab=AccumTab}, - %% Maybe set up the tracing - Config3 = - if Config2#opts.tracing == on, Node /= node() -> - %% Cannot trace on current node since the tracer will - %% trace itself - etop_tr:setup_tracer(Config2); - true -> - if Config2#opts.sort == runtime -> - Config2#opts{sort=reductions,tracing=off}; - true -> - Config2#opts{tracing=off} - end - end, - AccumTab = ets:new(accum_tab, - [set,public,{keypos,#etop_proc_info.pid}]), - Config4 = Config3#opts{accum_tab=AccumTab}, - - %% Start the output server - Out = spawn_link(Config4#opts.out_mod, init, [Config4]), - Config5 = Config4#opts{out_proc = Out}, - - init_data_handler(Config5), - ok. + {ok, Config4}. check_runtime_tools_vsn(Node) -> - case rpc:call(Node,observer_backend,vsn,[]) of - {ok,Vsn} -> check_vsn(Vsn); - _ -> exit("Faulty version of runtime_tools on remote node") - end. + case rpc:call(Node,observer_backend,vsn,[]) of + {ok,Vsn} -> check_vsn(Vsn); + _ -> exit("Faulty version of runtime_tools on remote node") + end. check_vsn(_Vsn) -> ok. -%check_vsn(_Vsn) -> exit("Faulty version of runtime_tools on remote node"). - %% Handle the incoming data -init_data_handler(Config) -> - register(?SERVER,self()), - Reader = - if Config#opts.tracing == on -> etop_tr:reader(Config); - true -> undefined - end, - data_handler(Reader, Config). - -data_handler(Reader, Opts) -> - receive - stop -> - stop(Opts), - ok; - {config,{Key,Value}} -> - data_handler(Reader,putopt(Key,Value,Opts)); - {dump,Fd} -> - Opts#opts.out_proc ! {dump,Fd}, - data_handler(Reader,Opts); - {'EXIT', EPid, Reason} when EPid == Opts#opts.out_proc -> - case Reason of - normal -> ok; - _ -> io:format("Output server crashed: ~p~n",[Reason]) - end, - stop(Opts), - out_proc_stopped; - {'EXIT', Reader, eof} -> - io:format("Lost connection to node ~p exiting~n", [Opts#opts.node]), - stop(Opts), - connection_lost; - _ -> - data_handler(Reader, Opts) - end. +update() -> + gen_server:call(?SERVER, update). -stop(Opts) -> - (Opts#opts.out_mod):stop(Opts#opts.out_proc), - if Opts#opts.tracing == on -> etop_tr:stop_tracer(Opts); - true -> ok - end, - unregister(?SERVER). +handle_call(update, _From, State) -> + Info = update(State), + Json = update_json(Info, State), + {reply, Json, State}; +handle_call({config, Key, Value}, _From, State) -> + State2 = putopt(Key, Value, State), + {reply, ok, State2}. update(#opts{store=Store,node=Node,tracing=Tracing}=Opts) -> - Pid = spawn_link(Node,observer_backend,etop_collect,[self()]), - Info = receive {Pid,I} -> I - after 1000 -> exit(connection_lost) - end, - #etop_info{procinfo=ProcInfo} = Info, - ProcInfo1 = - if Tracing == on -> - PI=lists:map(fun(PI=#etop_proc_info{pid=P}) -> - case ets:lookup(Store,P) of - [{P,T}] -> PI#etop_proc_info{runtime=T}; - [] -> PI - end - end, - ProcInfo), - PI; - true -> - lists:map(fun(PI) -> PI#etop_proc_info{runtime='-'} end,ProcInfo) - end, - ProcInfo2 = sort(Opts,ProcInfo1), - Info#etop_info{procinfo=ProcInfo2}. + Pid = spawn_link(Node,observer_backend,etop_collect,[self()]), + Info = receive {Pid,I} -> I + after 1000 -> exit(connection_lost) + end, + #etop_info{procinfo=ProcInfo} = Info, + ProcInfo1 = + if Tracing == on -> + PI=lists:map(fun(PI=#etop_proc_info{pid=P}) -> + case ets:lookup(Store,P) of + [{P,T}] -> PI#etop_proc_info{runtime=T}; + [] -> PI + end + end, + ProcInfo), + PI; + true -> + lists:map(fun(PI) -> PI#etop_proc_info{runtime='-'} end,ProcInfo) + end, + ProcInfo2 = sort(Opts,ProcInfo1), + Info#etop_info{procinfo=ProcInfo2}. sort(Opts,PI) -> - Tag = get_tag(Opts#opts.sort), - PI1 = if Opts#opts.accum -> - PI; - true -> - AccumTab = Opts#opts.accum_tab, - lists:map( - fun(#etop_proc_info{pid=Pid,reds=Reds,runtime=RT}=I) -> - NewI = - case ets:lookup(AccumTab,Pid) of - [#etop_proc_info{reds=OldReds, - runtime='-'}] -> - I#etop_proc_info{reds=Reds-OldReds, - runtime='-'}; - [#etop_proc_info{reds=OldReds, - runtime=OldRT}] -> - I#etop_proc_info{reds=Reds-OldReds, - runtime=RT-OldRT}; - [] -> - I - end, - ets:insert(AccumTab,I), - NewI - end, - PI) - end, - PI2 = lists:reverse(lists:keysort(Tag,PI1)), - lists:sublist(PI2,Opts#opts.lines). + Tag = get_tag(Opts#opts.sort), + PI1 = if Opts#opts.accum -> + PI; + true -> + AccumTab = Opts#opts.accum_tab, + lists:map( + fun(#etop_proc_info{pid=Pid,reds=Reds,runtime=RT}=I) -> + NewI = + case ets:lookup(AccumTab,Pid) of + [#etop_proc_info{reds=OldReds, + runtime='-'}] -> + I#etop_proc_info{reds=Reds-OldReds, + runtime='-'}; + [#etop_proc_info{reds=OldReds, + runtime=OldRT}] -> + I#etop_proc_info{reds=Reds-OldReds, + runtime=RT-OldRT}; + [] -> + I + end, + ets:insert(AccumTab,I), + NewI + end, + PI) + end, + PI2 = lists:reverse(lists:keysort(Tag,PI1)), + lists:sublist(PI2,Opts#opts.lines). get_tag(runtime) -> #etop_proc_info.runtime; get_tag(memory) -> #etop_proc_info.mem; @@ -247,102 +220,143 @@ get_tag(msg_q) -> #etop_proc_info.mq. %%% Configuration Management getopt(What, Config) when is_record(Config, opts) -> - case What of - node -> Config#opts.node; - port -> Config#opts.port; - accum -> Config#opts.accum; - intv -> Config#opts.intv; - lines -> Config#opts.lines; - sort -> Config#opts.sort; - width -> Config#opts.width; - height-> Config#opts.height; - - store -> Config#opts.store; - host -> Config#opts.host - end. + case What of + node -> Config#opts.node; + port -> Config#opts.port; + accum -> Config#opts.accum; + intv -> Config#opts.intv; + lines -> Config#opts.lines; + sort -> Config#opts.sort; + width -> Config#opts.width; + height-> Config#opts.height; + + store -> Config#opts.store; + host -> Config#opts.host + end. putopt(Key, Value, Config) when is_record(Config, opts) -> - Config1 = handle_args([{Key,Value}],Config), - Config1#opts.out_proc ! {config,{Key,Value},Config1}, - Config1. + Config1 = handle_args([{Key,Value}],Config), + Config1. handle_args([{node, [NodeString]}| R], Config) when is_list(NodeString) -> - Node = list_to_atom(NodeString), - NewC = Config#opts{node = Node}, - handle_args(R, NewC); + Node = list_to_atom(NodeString), + NewC = Config#opts{node = Node}, + handle_args(R, NewC); handle_args([{node, Node} |R], Config) when is_atom(Node) -> - NewC = Config#opts{node = Node}, - handle_args(R, NewC); + NewC = Config#opts{node = Node}, + handle_args(R, NewC); handle_args([{port, Port}| R], Config) when is_integer(Port) -> - NewC = Config#opts{port=Port}, - handle_args(R, NewC); + NewC = Config#opts{port=Port}, + handle_args(R, NewC); handle_args([{port, [Port]}| R], Config) when is_list(Port) -> - NewC = Config#opts{port= list_to_integer(Port)}, - handle_args(R, NewC); + NewC = Config#opts{port= list_to_integer(Port)}, + handle_args(R, NewC); handle_args([{interval, Time}| R], Config) when is_integer(Time)-> - NewC = Config#opts{intv=Time*1000}, - handle_args(R, NewC); + NewC = Config#opts{intv=Time*1000}, + handle_args(R, NewC); handle_args([{interval, [Time]}| R], Config) when is_list(Time)-> - NewC = Config#opts{intv=list_to_integer(Time)*1000}, - handle_args(R, NewC); + NewC = Config#opts{intv=list_to_integer(Time)*1000}, + handle_args(R, NewC); handle_args([{lines, Lines}| R], Config) when is_integer(Lines) -> - NewC = Config#opts{lines=Lines}, - handle_args(R, NewC); + NewC = Config#opts{lines=Lines}, + handle_args(R, NewC); handle_args([{lines, [Lines]}| R], Config) when is_list(Lines) -> - NewC = Config#opts{lines= list_to_integer(Lines)}, - handle_args(R, NewC); + NewC = Config#opts{lines= list_to_integer(Lines)}, + handle_args(R, NewC); handle_args([{accumulate, Bool}| R], Config) when is_atom(Bool) -> - NewC = Config#opts{accum=Bool}, - handle_args(R, NewC); + NewC = Config#opts{accum=Bool}, + handle_args(R, NewC); handle_args([{accumulate, [Bool]}| R], Config) when is_list(Bool) -> - NewC = Config#opts{accum= list_to_atom(Bool)}, - handle_args(R, NewC); + NewC = Config#opts{accum= list_to_atom(Bool)}, + handle_args(R, NewC); handle_args([{sort, Sort}| R], Config) when is_atom(Sort) -> - NewC = Config#opts{sort=Sort}, - handle_args(R, NewC); + NewC = Config#opts{sort=Sort}, + handle_args(R, NewC); handle_args([{sort, [Sort]}| R], Config) when is_list(Sort) -> - NewC = Config#opts{sort= list_to_atom(Sort)}, - handle_args(R, NewC); -handle_args([{output, Output}| R], Config) when is_atom(Output) -> - NewC = Config#opts{out_mod=output(Output)}, - handle_args(R, NewC); -handle_args([{output, [Output]}| R], Config) when is_list(Output) -> - NewC = Config#opts{out_mod= output(list_to_atom(Output))}, - handle_args(R, NewC); + NewC = Config#opts{sort= list_to_atom(Sort)}, + handle_args(R, NewC); handle_args([{tracing, OnOff}| R], Config) when is_atom(OnOff) -> - NewC = Config#opts{tracing=OnOff}, - handle_args(R, NewC); + NewC = Config#opts{tracing=OnOff}, + handle_args(R, NewC); handle_args([{tracing, [OnOff]}| R], Config) when is_list(OnOff) -> - NewC = Config#opts{tracing=list_to_atom(OnOff)}, - handle_args(R, NewC); + NewC = Config#opts{tracing=list_to_atom(OnOff)}, + handle_args(R, NewC); handle_args([_| R], C) -> - handle_args(R, C); + handle_args(R, C); handle_args([], C) -> - C. - -output(graphical) -> etop_gui; -output(text) -> etop_txt; -output(json) -> etop_json. - + C. loadinfo(SysI) -> - #etop_info{n_procs = Procs, - run_queue = RQ, - now = Now, - wall_clock = {_, WC}, - runtime = {_, RT}} = SysI, - Cpu = round(100*RT/WC), - Clock = io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w", - tuple_to_list(element(2,calendar:now_to_datetime(Now)))), - {Cpu,Procs,RQ,Clock}. + #etop_info{n_procs = Procs, + run_queue = RQ, + now = Now, + wall_clock = {_, WC}, + runtime = {_, RT}} = SysI, + Cpu = round(100*RT/WC), + Clock = io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w", + tuple_to_list(element(2,calendar:now_to_datetime(Now)))), + {Cpu,Procs,RQ,Clock}. meminfo(MemI, [Tag|Tags]) -> - [round(get_mem(Tag, MemI)/1024)|meminfo(MemI, Tags)]; + [round(get_mem(Tag, MemI)/1024)|meminfo(MemI, Tags)]; meminfo(_MemI, []) -> []. get_mem(Tag, MemI) -> - case lists:keysearch(Tag, 1, MemI) of - {value, {Tag, I}} -> I; %these are in bytes - _ -> 0 - end. + case lists:keysearch(Tag, 1, MemI) of + {value, {Tag, I}} -> I; %these are in bytes + _ -> 0 + end. + +update_json(Info, Config) -> + {Cpu,NProcs,RQ,Clock} = loadinfo(Info), + Header = + case Info#etop_info.memi of + undefined -> + [{<<"node">>, Config#opts.node}, + {<<"clock">>, iolist_to_binary(Clock)}, + {<<"cpu">>, Cpu}, + {<<"nprocs">>, NProcs}, + {<<"runqueue">>, RQ}]; + Memi -> + [Tot,Procs,Atom,Bin,Code,Ets] = + etop2:meminfo(Memi, [total,processes,atom,binary,code,ets]), + [{<<"node">>, Config#opts.node}, + {<<"clock">>, iolist_to_binary(Clock)}, + {<<"cpu">>, Cpu}, + {<<"tot">>, Tot}, + {<<"bin">>, Bin}, + {<<"nprocs">>, NProcs}, + {<<"procs">>, Procs}, + {<<"code">>, Code}, + {<<"runqueue">>, RQ}, + {<<"atom">>, Atom}, + {<<"ets">>, Ets}] + end, + Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], + [{<<"header">>, Header}, + {<<"procs">>, Ps}]. + +formatmfa({M, F, A}) -> + io_lib:format("~w:~w/~w",[M, F, A]). + +to_list(Name) when is_atom(Name) -> atom_to_list(Name); +to_list({_M,_F,_A}=MFA) -> formatmfa(MFA). + +etop_proc_info_to_json( + #etop_proc_info{pid=Pid, + mem=Mem, + reds=Reds, + name=Name, + runtime=Time, + cf=MFA, + mq=MQ}) -> + PInfo = + [{<<"pid">>, Pid}, + {<<"name">>, to_list(Name)}, + {<<"time">>, Time}, + {<<"reds">>, Reds}, + {<<"mem">>, Mem}, + {<<"mq">>, MQ}, + {<<"mfa">>, formatmfa(MFA)}], + {<<"pinfo">>, PInfo}. diff --git a/apps/bigwig/src/etop2_json.erl b/apps/bigwig/src/etop2_json.erl deleted file mode 100644 index a8f3294..0000000 --- a/apps/bigwig/src/etop2_json.erl +++ /dev/null @@ -1,103 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - --module(etop2_json). --author('siri@erix.ericsson.se'). - --export([init/1,stop/1]). --export([do_update/3]). - --include_lib("etop.hrl"). --include("etop_defs.hrl"). - -stop(Pid) -> Pid ! stop. - -init(Config) -> - loop(Config). - -loop(Config) -> - Info = do_update(Config), - receive - stop -> stopped; - {dump,Fd} -> do_update(Fd,Info,Config), loop(Config); - {config,_,Config1} -> loop(Config1) - after Config#opts.intv -> loop(Config) - end. - -do_update(Config) -> - Info = etop:update(Config), - do_update(standard_io,Info,Config). - -do_update(Fd,Info,Config) -> - {Cpu,NProcs,RQ,Clock} = etop2:loadinfo(Info), - Header = - case Info#etop_info.memi of - undefined -> - H = - [{<<"node">>, Config#opts.node}, - {<<"clock">>, iolist_to_binary(Clock)}, - {<<"cpu">>, Cpu}, - {<<"nprocs">>, NProcs}, - {<<"runqueue">>, RQ}], - {<<"header">>, H}; - Memi -> - [Tot,Procs,Atom,Bin,Code,Ets] = - etop2:meminfo(Memi, [total,processes,atom,binary,code,ets]), - H = - [{<<"node">>, Config#opts.node}, - {<<"clock">>, iolist_to_binary(Clock)}, - {<<"cpu">>, Cpu}, - {<<"tot">>, Tot}, - {<<"bin">>, Bin}, - {<<"nprocs">>, NProcs}, - {<<"procs">>, Procs}, - {<<"code">>, Code}, - {<<"runqueue">>, RQ}, - {<<"atom">>, Atom}, - {<<"ets">>, Ets}], - {<<"header">>, H} - end, - Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], - io:fwrite(Fd, "~p", [Header]), - io:fwrite(Fd, "~p", [Ps]), - Info. - -formatmfa({M, F, A}) -> - io_lib:format("~w:~w/~w",[M, F, A]). - -to_list(Name) when is_atom(Name) -> atom_to_list(Name); -to_list({_M,_F,_A}=MFA) -> formatmfa(MFA). - -etop_proc_info_to_json( - #etop_proc_info{pid=Pid, - mem=Mem, - reds=Reds, - name=Name, - runtime=Time, - cf=MFA, - mq=MQ}) -> - PInfo = - [{<<"pid">>, Pid}, - {<<"name">>, to_list(Name)}, - {<<"time">>, Time}, - {<<"reds">>, Reds}, - {<<"mem">>, Mem}, - {<<"mq">>, MQ}, - {<<"mfa">>, formatmfa(MFA)}], - {<<"pinfo">>, PInfo}. From 6ed131c9ab4ff5efb18285b7d650b9824c4c742b Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 15:48:46 +0100 Subject: [PATCH 016/405] Add bigwig_http_pid for GETting process info --- apps/bigwig/src/bigwig_http.erl | 1 + apps/bigwig/src/bigwig_http_pid.erl | 55 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 apps/bigwig/src/bigwig_http_pid.erl diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl index f45d1da..0c55683 100644 --- a/apps/bigwig/src/bigwig_http.erl +++ b/apps/bigwig/src/bigwig_http.erl @@ -29,6 +29,7 @@ dispatch_rules() -> , {[<<"wh">>, '...'], bigwig_http_wh, []} , {[<<"vm">>], bigwig_http_vm, []} , {[<<"rb">>, '...'], bigwig_http_rb, []} + , {[<<"pid">>, '...'], bigwig_http_pid, []} , {'_', bigwig_http_catchall, []} ]}]. diff --git a/apps/bigwig/src/bigwig_http_pid.erl b/apps/bigwig/src/bigwig_http_pid.erl new file mode 100644 index 0000000..cf63cac --- /dev/null +++ b/apps/bigwig/src/bigwig_http_pid.erl @@ -0,0 +1,55 @@ +%% +%% show details on a specific process +%% +-module(bigwig_http_pid). +-behaviour(cowboy_http_handler). +-export([init/3, handle/2, terminate/2]). + +init({tcp, http}, Req, _Opts) -> + {ok, Req, undefined_state}. + +handle(Req0, State) -> + {Path, Req} = cowboy_http_req:path(Req0), + {Method, Req1} = cowboy_http_req:method(Req), + handle_path(Method, Path, Req1, State). + +handle_path('GET', [<<"pid">>, <<"global">>, Name], Req, State) -> + handle_get_pid(fun to_global_pid/1, Name, Req, State); +handle_path('GET', [<<"pid">>, Pid], Req, State) -> + handle_get_pid(fun to_pid/1, Pid, Req, State); +handle_path(_, _, Req, State) -> + not_found(Req, State). + +not_found(Req, State) -> + {ok, Req2} = cowboy_http_req:reply(404, [], <<"

404

">>, Req), + {ok, Req2, State}. + +terminate(_Req, _State) -> + ok. + +handle_get_pid(Get, Pid0, Req, State) -> + case catch(Get(Pid0)) of + Pid when is_pid(Pid) -> pid_response(Pid, Req, State); + _ -> not_found(Req, State) + end. + +-spec to_pid(binary()) -> pid() | undefined. +to_pid(Bin) when is_binary(Bin) -> + L = binary_to_list(Bin), + try list_to_pid([$<] ++ L ++ [$>]) + catch error:badarg -> whereis(list_to_existing_atom(L)) + end. + +-spec to_global_pid(binary()) -> pid() | undefined. +to_global_pid(Name) -> + global:whereis_name(list_to_existing_atom(binary_to_list(Name))). + +pid_response(Pid, Req, State) -> + case erlang:process_info(Pid) of + undefined -> not_found(Req, State); + Info -> + Body = jsx:term_to_json(Info), + Headers = [{<<"Content-Type">>, <<"application/json">>}], + {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2, State} + end. From d79728c04c73cefdb9966b47405bf11f3e202838 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Sat, 9 Jul 2011 15:56:55 +0100 Subject: [PATCH 017/405] more tidying/OTP --- apps/bigwig/include/etop_defs.hrl | 2 +- apps/bigwig/src/etop2.erl | 191 +++++++++++++----------------- 2 files changed, 81 insertions(+), 112 deletions(-) diff --git a/apps/bigwig/include/etop_defs.hrl b/apps/bigwig/include/etop_defs.hrl index 4bb83b9..f784554 100644 --- a/apps/bigwig/include/etop_defs.hrl +++ b/apps/bigwig/include/etop_defs.hrl @@ -25,5 +25,5 @@ -record(opts, {node=node(), port = 8415, accum = false, intv = 5000, lines = 10, width = 700, height = 340, sort = runtime, tracing = on, %% Other state information - server, host, tracer, store, + host, tracer, store, accum_tab, remote}). diff --git a/apps/bigwig/src/etop2.erl b/apps/bigwig/src/etop2.erl index 2f488b1..bb8c129 100644 --- a/apps/bigwig/src/etop2.erl +++ b/apps/bigwig/src/etop2.erl @@ -27,70 +27,43 @@ %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). --export([config/2, help/0]). %% Internal --export([update/1]). --export([loadinfo/1, meminfo/2, getopt/2]). +-export([loadinfo/1, meminfo/2]). -include("etop.hrl"). -include("etop_defs.hrl"). --define(change_at_runtime_config,[lines,interval,sort,accumulate]). +-define(change_at_runtime_config,[lines,sort,accumulate]). -define(SERVER, etop2_server). -help() -> - io:format( - "Usage of the erlang top program~n" - "Options are set as command line parameters as in -node a@host -..~n" - "or as parameter to etop:start([{node, a@host}, {...}])~n" - "Options are:~n" - " node atom Required The erlang node to measure ~n" - " port integer The used port, NOTE: due to a bug this program~n" - " will hang if the port is not avaiable~n" - " accumulate boolean If true execution time is accumulated ~n" - " lines integer Number of displayed processes~n" - " interval integer Display update interval in secs~n" - " sort runtime | reductions | memory | msg_q~n" - " What information to sort by~n" - " Default: runtime (reductions if tracing=off)~n" - " output graphical | text~n" - " How to present results~n" - " Default: graphical~n" - " tracing on | off etop uses the erlang trace facility, and thus~n" - " no other tracing is possible on the node while~n" - " etop is running, unless this option is set to~n" - " 'off'. Also helpful if the etop tracing causes~n" - " too high load on the measured node.~n" - " With tracing off, runtime is not measured!~n" - " setcookie string Only applicable on operating system command~n" - " line. Set cookie for the etop node, must be~n" - " same as the cookie for the measured node.~n" - " This is not an etop parameter~n" - ). - -terminate(_Reason, State) -> - %% TODO: port over tracer? - if State#opts.tracing == on -> etop_tr:stop_tracer(State); - true -> ok - end. - -config(Key,Value) -> - case check_runtime_config(Key,Value) of - ok -> - ?SERVER ! {config,{Key,Value}}, - ok; - error -> - {error,illegal_opt} - end. -check_runtime_config(lines,L) when is_integer(L),L>0 -> ok; -check_runtime_config(interval,I) when is_integer(I),I>0 -> ok; -check_runtime_config(sort,S) when S=:=runtime; - S=:=reductions; - S=:=memory; - S=:=msg_q -> ok; -check_runtime_config(accumulate,A) when A=:=true; A=:=false -> ok; -check_runtime_config(_Key,_Value) -> error. +%% Usage of the erlang top program +%% Options are set as command line parameters as in -node a@host -.. +%% or as parameter to etop:start([{node, a@host}, {...}]) +%% Options are: +%% node atom Required The erlang node to measure +%% port integer The used port, NOTE: due to a bug this program +%% will hang if the port is not avaiable +%% accumulate boolean If true execution time is accumulated +%% lines integer Number of displayed processes +%% sort runtime | reductions | memory | msg_q +%% What information to sort by +%% Default: runtime (reductions if tracing=off) +%% tracing on | off etop uses the erlang trace facility, and thus +%% no other tracing is possible on the node while +%% etop is running, unless this option is set to +%% 'off'. Also helpful if the etop tracing causes +%% too high load on the measured node. +%% With tracing off, runtime is not measured! +%% setcookie string Only applicable on operating system command +%% line. Set cookie for the etop node, must be +%% same as the cookie for the measured node. +%% This is not an etop parameter + +terminate(_Reason, State#opts{tracing=on}) -> + etop_tr:stop_tracer(State); +terminate(_Reason, _) -> + ok. start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). @@ -106,43 +79,28 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. init(Opts) -> - Config1 = handle_args(init:get_arguments() ++ Opts, #opts{}), - Config2 = Config1#opts{server=self()}, + Config1 = #opts{node=Node} = handle_args(Opts, #opts{}), %% Connect to the node we want to look at - Node = getopt(node, Config2), - case net_adm:ping(Node) of - pang when Node /= node() -> - io:format("Error Couldn't connect to node ~p ~n~n", [Node]), - help(), - exit("connection error"); - _pong -> - check_runtime_tools_vsn(Node) - end, - - %% Maybe set up the tracing - Config3 = - if Config2#opts.tracing == on, Node /= node() -> - %% Cannot trace on current node since the tracer will - %% trace itself - etop_tr:setup_tracer(Config2); - true -> - if Config2#opts.sort == runtime -> - Config2#opts{sort=reductions,tracing=off}; - true -> - Config2#opts{tracing=off} - end - end, - AccumTab = ets:new(accum_tab, - [set,public,{keypos,#etop_proc_info.pid}]), - Config4 = Config3#opts{accum_tab=AccumTab}, - - {ok, Config4}. + %% TODO: Move the connect elsewhere so we can switch nodes + case connect(Node) of + ok -> + %% Maybe set up the tracing + Config2 = setup_tracing(Config1), + + AccumTab = ets:new(accum_tab, + [set,public,{keypos,#etop_proc_info.pid}]), + Config3 = Config2#opts{accum_tab=AccumTab}, + + {ok, Config3}; + Err -> + Err + end. check_runtime_tools_vsn(Node) -> case rpc:call(Node,observer_backend,vsn,[]) of - {ok,Vsn} -> check_vsn(Vsn); - _ -> exit("Faulty version of runtime_tools on remote node") + {ok, Vsn} -> check_vsn(Vsn); + _ -> {error, "faulty version of runtime_tools on remote node"} end. check_vsn(_Vsn) -> ok. @@ -151,13 +109,24 @@ check_vsn(_Vsn) -> ok. update() -> gen_server:call(?SERVER, update). +check_runtime_config(lines,L) when is_integer(L),L>0 -> ok; +check_runtime_config(sort,S) + when S=:=runtime; S=:=reductions; S=:=memory; S=:=msg_q -> ok; +check_runtime_config(accumulate,A) when A=:=true; A=:=false -> ok; +check_runtime_config(_Key,_Value) -> error. + handle_call(update, _From, State) -> Info = update(State), Json = update_json(Info, State), {reply, Json, State}; handle_call({config, Key, Value}, _From, State) -> - State2 = putopt(Key, Value, State), - {reply, ok, State2}. + case check_runtime_config(Key, Value) of + error -> + {reply, error, State}; + _ -> + State2 = putopt(Key, Value, State), + {reply, ok, State2} + end. update(#opts{store=Store,node=Node,tracing=Tracing}=Opts) -> Pid = spawn_link(Node,observer_backend,etop_collect,[self()]), @@ -219,21 +188,6 @@ get_tag(msg_q) -> #etop_proc_info.mq. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Configuration Management -getopt(What, Config) when is_record(Config, opts) -> - case What of - node -> Config#opts.node; - port -> Config#opts.port; - accum -> Config#opts.accum; - intv -> Config#opts.intv; - lines -> Config#opts.lines; - sort -> Config#opts.sort; - width -> Config#opts.width; - height-> Config#opts.height; - - store -> Config#opts.store; - host -> Config#opts.host - end. - putopt(Key, Value, Config) when is_record(Config, opts) -> Config1 = handle_args([{Key,Value}],Config), Config1. @@ -251,12 +205,6 @@ handle_args([{port, Port}| R], Config) when is_integer(Port) -> handle_args([{port, [Port]}| R], Config) when is_list(Port) -> NewC = Config#opts{port= list_to_integer(Port)}, handle_args(R, NewC); -handle_args([{interval, Time}| R], Config) when is_integer(Time)-> - NewC = Config#opts{intv=Time*1000}, - handle_args(R, NewC); -handle_args([{interval, [Time]}| R], Config) when is_list(Time)-> - NewC = Config#opts{intv=list_to_integer(Time)*1000}, - handle_args(R, NewC); handle_args([{lines, Lines}| R], Config) when is_integer(Lines) -> NewC = Config#opts{lines=Lines}, handle_args(R, NewC); @@ -360,3 +308,24 @@ etop_proc_info_to_json( {<<"mq">>, MQ}, {<<"mfa">>, formatmfa(MFA)}], {<<"pinfo">>, PInfo}. + +connect(Node) -> + case net_adm:ping(Node) of + pang when Node /= node() -> + {error, "node connect failed"}; + _Pong -> + check_runtime_tools_vsn(Node) + end. + +setup_tracing(Config=#opts{tracing=T, sort=S, node=N}) -> + if T =:= on, N /= node() -> + %% Cannot trace on current node since the tracer will + %% trace itself + etop_tr:setup_tracer(Config); + true -> + if S =:= runtime -> + Config#opts{sort=reductions, tracing=off}; + true -> + Config#opts{tracing=off} + end + end. From 6119f4f767b4def548dbd67c864a324698df3db6 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Sat, 9 Jul 2011 16:01:11 +0100 Subject: [PATCH 018/405] fix compilation error --- apps/bigwig/src/etop2.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/bigwig/src/etop2.erl b/apps/bigwig/src/etop2.erl index bb8c129..c18bf8d 100644 --- a/apps/bigwig/src/etop2.erl +++ b/apps/bigwig/src/etop2.erl @@ -60,7 +60,7 @@ %% same as the cookie for the measured node. %% This is not an etop parameter -terminate(_Reason, State#opts{tracing=on}) -> +terminate(_Reason, State=#opts{tracing=on}) -> etop_tr:stop_tracer(State); terminate(_Reason, _) -> ok. @@ -188,9 +188,8 @@ get_tag(msg_q) -> #etop_proc_info.mq. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Configuration Management -putopt(Key, Value, Config) when is_record(Config, opts) -> - Config1 = handle_args([{Key,Value}],Config), - Config1. +putopt(Key, Value, #opts{} = Config) -> + handle_args([{Key, Value}], Config). handle_args([{node, [NodeString]}| R], Config) when is_list(NodeString) -> Node = list_to_atom(NodeString), From a42260cfe1e8cefa12385972d5ef2a2921c39464 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 13:41:57 +0100 Subject: [PATCH 019/405] add ~ swp files to ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 928d8bf..fe25088 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ apps/*/ebin/*.app deps/ *.swp log/ +*~ From 04349bd0b2222616b503c837cc4b03fd9b5caa66 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 16:04:00 +0100 Subject: [PATCH 020/405] add some urls to readme --- README | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README b/README index 3ff0190..c4251f3 100644 --- a/README +++ b/README @@ -13,3 +13,13 @@ applications, and VM settings. Conceived for Spawnfest 2011, SMELLS LIKE BEAM SPIRIT. +=========== + +/vm lists running apps, releases, process count etc + +/rb/reports lists available SASL reports +/rb/reports/1 returns SASL report id 1 + +/pid/<1.2.3> process_info (POST to send msg to pid) +/module/NAME NAME:module_info() stuff + From 35b9998440b3a47291edab775c9379488d5f5aa6 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 16:04:18 +0100 Subject: [PATCH 021/405] add custom SASL error_logger handler --- apps/bigwig/src/bigwig_error_handler.erl | 51 ++++++++++++++++++++ apps/bigwig/src/bigwig_error_handler_mon.erl | 42 ++++++++++++++++ apps/bigwig/src/bigwig_sup.erl | 5 +- 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 apps/bigwig/src/bigwig_error_handler.erl create mode 100644 apps/bigwig/src/bigwig_error_handler_mon.erl diff --git a/apps/bigwig/src/bigwig_error_handler.erl b/apps/bigwig/src/bigwig_error_handler.erl new file mode 100644 index 0000000..51fb7a2 --- /dev/null +++ b/apps/bigwig/src/bigwig_error_handler.erl @@ -0,0 +1,51 @@ +%% +%% Gets added as a handler to SASL's error_logger +%% Broadcasts SASL reports to all registered client pids +%% +%% Used to stream new reports down a websocket. +%% +-module(bigwig_error_handler). +-behaviour(gen_event). + +-export([add_sup_handler/0, register_client/1]). + +%% gen_event callbacks +-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]). + +-record(state, {listeners=[]}). + +%%%---------------------------------------------------------------------------- + +add_sup_handler() -> + gen_event:add_sup_handler(error_logger, ?MODULE, []). + +register_client(Pid) -> + gen_event:call(error_logger, ?MODULE, {register_client, Pid}). + +%%%---------------------------------------------------------------------------- + +init([]) -> {ok, #state{}}. + +handle_event(Event, State) -> + io:format("EVENT(to ~p) ~p\n", [length(State#state.listeners), Event]), + lists:foreach(fun(Pid) -> Pid ! Event end, State#state.listeners), + {ok, State}. + +handle_call({register_client, Pid}, State) -> + NewListeners = [ Pid | State#state.listeners ], + erlang:monitor(process, Pid), + {ok, ok, State#state{ listeners=NewListeners }}; + +handle_call(_Msg, State) -> {ok, not_implemented, State}. + +handle_info({'DOWN', _MonitorRef, process, Pid, _Reason}, State) -> + NewListeners = lists:remove(Pid, State#state.listeners), + {ok, ok, State#state{ listeners=NewListeners }}; + +handle_info(_Info, State) -> {ok, State}. + +terminate(_Reason, _State) -> ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + diff --git a/apps/bigwig/src/bigwig_error_handler_mon.erl b/apps/bigwig/src/bigwig_error_handler_mon.erl new file mode 100644 index 0000000..729ce58 --- /dev/null +++ b/apps/bigwig/src/bigwig_error_handler_mon.erl @@ -0,0 +1,42 @@ +%% +%% Used to add and catch crashes of our own error handler +%% +-module(bigwig_error_handler_mon). +-behaviour(gen_server). + +-export([start_link/0, stop/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +-record(state, {}). + +start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +stop() -> gen_event:call(?MODULE, stop, infinity). + +%%%---------------------------------------------------------------------------- + +init([]) -> + %% Add our error handler + ok = bigwig_error_handler:add_sup_handler(), + {ok, #state{}}. + +handle_call(stop, _From, State) -> {stop, normal, ok, State}; +handle_call(_Request, _From, State) -> {reply, not_implemented, State}. + +handle_cast(_Msg, State) -> {noreply, State}. + +handle_info({gen_event_EXIT, Handler, Reason}, State) -> + %% Our handler crashed, so we crash, allowing supervisor to restart us + %% which will re-add the handler. + io:format("bigwig error handler ~p crashed: ~p (pid ~p)", + [Handler, Reason, self()]), + {stop, gen_event_EXIT, State}; + +handle_info(_Info, State) -> {noreply, State}. + +terminate(_Reason, _State) -> ok. + +code_change(_OldVsn, State, _Extra) -> {ok, State}. + diff --git a/apps/bigwig/src/bigwig_sup.erl b/apps/bigwig/src/bigwig_sup.erl index 5fb7407..54cfe78 100644 --- a/apps/bigwig/src/bigwig_sup.erl +++ b/apps/bigwig/src/bigwig_sup.erl @@ -23,9 +23,10 @@ start_link() -> %% =================================================================== init([]) -> - Http = ?CHILD(bigwig_http, worker), + Http = ?CHILD(bigwig_http, worker), + ErrMon = ?CHILD(bigwig_error_handler_mon, worker), - Specs = [ Http ], + Specs = [ ErrMon, Http ], {ok, { {one_for_one, 5, 10}, Specs} }. From df9ca637d6667fe79f2a72d50fc7291af2336098 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 16:09:23 +0100 Subject: [PATCH 022/405] Add bigwig_util for parsing terms (and soon more!) --- apps/bigwig/src/bigwig_util.erl | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 apps/bigwig/src/bigwig_util.erl diff --git a/apps/bigwig/src/bigwig_util.erl b/apps/bigwig/src/bigwig_util.erl new file mode 100644 index 0000000..792cdaf --- /dev/null +++ b/apps/bigwig/src/bigwig_util.erl @@ -0,0 +1,23 @@ +%% +%% miscellaneous utilities +%% +-module(bigwig_util). + +-export([parse_term/1, ensure_dot/1]). + +-spec parse_term(binary() | list()) -> {ok, any()} | {error, any()}. +parse_term(Bin) when is_binary(Bin) -> + parse_term(binary_to_list(Bin)); +parse_term(Str) when is_list(Str) -> + case erl_scan:string(Str) of + {ok, Tokens0, _EndLoc} -> + Tokens = ensure_dot(Tokens0), + erl_parse:parse_term(Tokens) + end. + +-spec ensure_dot(erl_scan:tokens()) -> erl_scan:tokens(). +ensure_dot(Tokens) -> + case lists:last(Tokens) of + {dot, 1} -> Tokens; + _ -> Tokens ++ [{dot, 1}] + end. From 6226ee622924b3fb71bf34c5582a91d9f6ea747b Mon Sep 17 00:00:00 2001 From: Steven Gravell Date: Sat, 9 Jul 2011 16:13:14 +0100 Subject: [PATCH 023/405] make js generic --- apps/bigwig/priv/css/body.css | 6 +++ apps/bigwig/priv/html/index.html | 8 +++- apps/bigwig/priv/js/tpl.js | 60 +++++++++++++++++++++++------- apps/bigwig/src/bigwig_http_wh.erl | 26 ++++++++----- 4 files changed, 75 insertions(+), 25 deletions(-) diff --git a/apps/bigwig/priv/css/body.css b/apps/bigwig/priv/css/body.css index 817b37a..fbd7929 100644 --- a/apps/bigwig/priv/css/body.css +++ b/apps/bigwig/priv/css/body.css @@ -7,3 +7,9 @@ body { display: none; visibility: none; } + +#apps .name { +} +#apps .description { + color: #888; +} diff --git a/apps/bigwig/priv/html/index.html b/apps/bigwig/priv/html/index.html index 6dc416f..b651997 100644 --- a/apps/bigwig/priv/html/index.html +++ b/apps/bigwig/priv/html/index.html @@ -5,8 +5,12 @@

-
    -
  • +
      +
    • + + + +
    diff --git a/apps/bigwig/priv/js/tpl.js b/apps/bigwig/priv/js/tpl.js index 3ab24d7..926bfeb 100644 --- a/apps/bigwig/priv/js/tpl.js +++ b/apps/bigwig/priv/js/tpl.js @@ -6,33 +6,67 @@ tpl = (function() { } title("Loading..."); - var li = function(p, el) { - var id = el.id - var li = $('li[data-id='+id+']', p); - if(li.length==0) { - li = $('li._tpl',p).clone(false, false); - li.removeClass('_tpl'); - p.append(li); - } - li.text(id); - } var host = document.location.host; var sock = new WebSocket("ws://"+host+"/wh/ws"); sock.onopen = function() { + } + var visit = function(href) { + sock.send(JSON.stringify({visit:href})); + } + var href_click = function(href) { + return function() { + visit(href); + return false; + } + } + var li = function(type, p, data) { + var id = data.id; + var li = $('li[data-id='+id+']', p); + if(li.length==0) { + li = $('li._tpl',p).clone(false, false); + li.attr('data-id', id); + li.removeClass('_tpl'); + p.append(li); + } + var permalink = $('.permalink', li); + if(permalink) { + var uniq = {type: type, id: id}; + var href = "wh/"+type+"/"+id; + permalink.attr('href', href); + permalink.bind('click', href_click(uniq)); + } + for(var k in data) { + li.data(k, data[k]); + var child = $("."+k, li); + if(child) { + $("."+k, li).text(data[k]); + } + } + } + var update = function(k, data) { + var el = $('#'+k); + var jt = typeof data; + if(jt == "string") { + el.text(data); + } else if(jt == "object") { + for(var i=0; i error(websockets_only). websocket_init(_TransportName, Req, _Opts) -> - send({struct, [ - {title, <<"So Peter... What's Happening?">>}, - apps() - ]}), + send([ + {title, <<"What's Happening">>}, + apps() + ]), {ok, Req, undefined_state}. websocket_handle({timeout, _Ref, Msg}, Req, State) -> {reply, Msg, Req, State}; websocket_handle({websocket, Msg}, Req, State) -> - {reply, <<"Hello ", Msg/binary>>, Req, State}. + Term = jsx:json_to_term(Msg), + {reply, jsx:term_to_json(Term), Req, State}. websocket_terminate(_Reason, _Req, _State) -> ok. -send(T) when is_tuple(T) -> - send(iolist_to_binary(mochijson2:encode(T))); +send(T) when not is_binary(T) -> + send(jsx:term_to_json(T)); send(Bin) -> erlang:start_timer(0, self(), Bin). apps() -> - {apps, [ - {struct, [{id, App}]} - || {App, _Desc, _Vsn} <- application:which_applications() + {app, [ + [ + {id, App}, + {name, App}, + {description, list_to_binary(Desc)}, + {version, list_to_binary(Vsn)} + ] + || {App, Desc, Vsn} <- application:which_applications() ]}. From 65dde2a03609f3c7c2bd1f62b8241b4c42b6887d Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 16:30:38 +0100 Subject: [PATCH 024/405] Add POST handler for sending arbitrary terms to pids --- apps/bigwig/src/bigwig_http_pid.erl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/apps/bigwig/src/bigwig_http_pid.erl b/apps/bigwig/src/bigwig_http_pid.erl index cf63cac..c7fecb6 100644 --- a/apps/bigwig/src/bigwig_http_pid.erl +++ b/apps/bigwig/src/bigwig_http_pid.erl @@ -17,6 +17,10 @@ handle_path('GET', [<<"pid">>, <<"global">>, Name], Req, State) -> handle_get_pid(fun to_global_pid/1, Name, Req, State); handle_path('GET', [<<"pid">>, Pid], Req, State) -> handle_get_pid(fun to_pid/1, Pid, Req, State); +handle_path('POST', [<<"pid">>, <<"global">>, Name], Req, State) -> + handle_post_pid(fun to_global_pid/1, Name, Req, State); +handle_path('POST', [<<"pid">>, Pid], Req, State) -> + handle_post_pid(fun to_pid/1, Pid, Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). @@ -33,6 +37,30 @@ handle_get_pid(Get, Pid0, Req, State) -> _ -> not_found(Req, State) end. +handle_post_pid(Get, Pid0, Req, State) -> + case catch(Get(Pid0)) of + Pid when is_pid(Pid) -> + post_pid_response(Pid, Req, State); + _ -> not_found(Req, State) + end. + +post_pid_response(Pid, Req, State) -> + {Res, Req1} = cowboy_http_req:body_qs(Req), + {ok, Req2} = + case lists:keyfind(<<"msg">>, 1, Res) of + {<<"msg">>, TermStr} -> + case catch(bigwig_util:parse_term(TermStr)) of + {ok, Term} -> + Pid ! Term, + cowboy_http_req:reply(202, [], <<>>, Req1); + _ -> + cowboy_http_req:reply(400, [], <<"badarg">>, Req1) + end; + _ -> + cowboy_http_req:reply(400, [], <<"'msg' required">>, Req1) + end, + {ok, Req2, State}. + -spec to_pid(binary()) -> pid() | undefined. to_pid(Bin) when is_binary(Bin) -> L = binary_to_list(Bin), From cf26ba430695d494504c4cfa0a1bffd50e7be9d6 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 16:36:32 +0100 Subject: [PATCH 025/405] Return appropriate 202 for asynchronous sends --- apps/bigwig/src/bigwig_http_pid.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/bigwig/src/bigwig_http_pid.erl b/apps/bigwig/src/bigwig_http_pid.erl index c7fecb6..cf4e616 100644 --- a/apps/bigwig/src/bigwig_http_pid.erl +++ b/apps/bigwig/src/bigwig_http_pid.erl @@ -51,8 +51,9 @@ post_pid_response(Pid, Req, State) -> {<<"msg">>, TermStr} -> case catch(bigwig_util:parse_term(TermStr)) of {ok, Term} -> - Pid ! Term, - cowboy_http_req:reply(202, [], <<>>, Req1); + Body = io_lib:format("~p", [Pid ! Term]), + Headers = [{<<"Content-Type">>, <<"application/x-erlang-term">>}], + cowboy_http_req:reply(202, Headers, Body, Req1); _ -> cowboy_http_req:reply(400, [], <<"badarg">>, Req1) end; From 58c93b13e43af2f456ba212037ccd64375114a06 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 16:38:26 +0100 Subject: [PATCH 026/405] Return erlang terms for 400 errors also --- apps/bigwig/src/bigwig_http_pid.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/bigwig/src/bigwig_http_pid.erl b/apps/bigwig/src/bigwig_http_pid.erl index cf4e616..b16f6aa 100644 --- a/apps/bigwig/src/bigwig_http_pid.erl +++ b/apps/bigwig/src/bigwig_http_pid.erl @@ -46,19 +46,19 @@ handle_post_pid(Get, Pid0, Req, State) -> post_pid_response(Pid, Req, State) -> {Res, Req1} = cowboy_http_req:body_qs(Req), + Headers = [{<<"Content-Type">>, <<"application/x-erlang-term">>}], {ok, Req2} = case lists:keyfind(<<"msg">>, 1, Res) of {<<"msg">>, TermStr} -> case catch(bigwig_util:parse_term(TermStr)) of {ok, Term} -> Body = io_lib:format("~p", [Pid ! Term]), - Headers = [{<<"Content-Type">>, <<"application/x-erlang-term">>}], cowboy_http_req:reply(202, Headers, Body, Req1); _ -> - cowboy_http_req:reply(400, [], <<"badarg">>, Req1) + cowboy_http_req:reply(400, Headers, <<"{error, badarg}">>, Req1) end; _ -> - cowboy_http_req:reply(400, [], <<"'msg' required">>, Req1) + cowboy_http_req:reply(400, Headers, <<"{error, msg_required}">>, Req1) end, {ok, Req2, State}. From 6144beb0b49b33328ceb1e480db8874ed0f53a39 Mon Sep 17 00:00:00 2001 From: Steven Gravell Date: Sat, 9 Jul 2011 16:40:04 +0100 Subject: [PATCH 027/405] implement getting data --- apps/bigwig/priv/html/index.html | 1 + apps/bigwig/priv/js/tpl.js | 33 ++++++++++++++++++++---------- apps/bigwig/src/bigwig_http_wh.erl | 11 ++++++---- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/apps/bigwig/priv/html/index.html b/apps/bigwig/priv/html/index.html index b651997..ff86426 100644 --- a/apps/bigwig/priv/html/index.html +++ b/apps/bigwig/priv/html/index.html @@ -10,6 +10,7 @@

    +
diff --git a/apps/bigwig/priv/js/tpl.js b/apps/bigwig/priv/js/tpl.js index 926bfeb..6e03d31 100644 --- a/apps/bigwig/priv/js/tpl.js +++ b/apps/bigwig/priv/js/tpl.js @@ -1,6 +1,6 @@ -tpl = (function() { +TPL = (function() { var title = function(title) { $('#title').text(title); } @@ -20,6 +20,15 @@ tpl = (function() { return false; } } + var updateChildren = function(el, data) { + for(var k in data) { + el.data(k, data[k]); + var child = $("."+k, el); + if(child) { + $("."+k, el).text(data[k]); + } + } + } var li = function(type, p, data) { var id = data.id; var li = $('li[data-id='+id+']', p); @@ -28,6 +37,8 @@ tpl = (function() { li.attr('data-id', id); li.removeClass('_tpl'); p.append(li); + } else { + li = li.first(); } var permalink = $('.permalink', li); if(permalink) { @@ -36,29 +47,29 @@ tpl = (function() { permalink.attr('href', href); permalink.bind('click', href_click(uniq)); } - for(var k in data) { - li.data(k, data[k]); - var child = $("."+k, li); - if(child) { - $("."+k, li).text(data[k]); - } - } + updateChildren(li, data); } var update = function(k, data) { var el = $('#'+k); + if(el.length==0) { + el = $('*[data-id='+k+']').first(); + } var jt = typeof data; if(jt == "string") { el.text(data); } else if(jt == "object") { - for(var i=0; i websocket_handle({timeout, _Ref, Msg}, Req, State) -> {reply, Msg, Req, State}; -websocket_handle({websocket, Msg}, Req, State) -> - Term = jsx:json_to_term(Msg), +websocket_handle({websocket, _Msg}, Req, State) -> + %Term = jsx:json_to_term(Msg), + Term = [{bigwig, [ + {more, <<"WOOP">>} + ]}], {reply, jsx:term_to_json(Term), Req, State}. websocket_terminate(_Reason, _Req, _State) -> @@ -46,8 +49,8 @@ apps() -> [ {id, App}, {name, App}, - {description, list_to_binary(Desc)}, - {version, list_to_binary(Vsn)} + {description, Desc}, + {version, Vsn} ] || {App, Desc, Vsn} <- application:which_applications() ]}. From f9a7b3591809bff2f364c2b6c8819b9494611fe7 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 16:51:25 +0100 Subject: [PATCH 028/405] Add module info for /module/{module} --- apps/bigwig/src/bigwig_http.erl | 1 + apps/bigwig/src/bigwig_http_module.erl | 42 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 apps/bigwig/src/bigwig_http_module.erl diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl index 5c5ffcd..3218310 100644 --- a/apps/bigwig/src/bigwig_http.erl +++ b/apps/bigwig/src/bigwig_http.erl @@ -31,6 +31,7 @@ dispatch_rules() -> , {[<<"vm">>], bigwig_http_vm, []} , {[<<"rb">>, '...'], bigwig_http_rb, []} , {[<<"pid">>, '...'], bigwig_http_pid, []} + , {[<<"module">>, '...'], bigwig_http_module, []} , {'_', bigwig_http_catchall, []} ]}]. diff --git a/apps/bigwig/src/bigwig_http_module.erl b/apps/bigwig/src/bigwig_http_module.erl new file mode 100644 index 0000000..a3a3054 --- /dev/null +++ b/apps/bigwig/src/bigwig_http_module.erl @@ -0,0 +1,42 @@ +%% +%% show details on a specific module +%% +-module(bigwig_http_module). +-behaviour(cowboy_http_handler). +-export([init/3, handle/2, terminate/2]). + +init({tcp, http}, Req, _Opts) -> + {ok, Req, undefined_state}. + +handle(Req0, State) -> + {Path, Req} = cowboy_http_req:path(Req0), + {Method, Req1} = cowboy_http_req:method(Req), + handle_path(Method, Path, Req1, State). + +handle_path('GET', [<<"module">>, Module], Req, State) -> + case to_module_info(Module) of + [] -> not_found(Req, State); + Info -> json_response(Info, Req, State) + end; +handle_path(_, _, Req, State) -> + not_found(Req, State). + +not_found(Req, State) -> + {ok, Req2} = cowboy_http_req:reply(404, [], <<"

404

">>, Req), + {ok, Req2, State}. + +terminate(_Req, _State) -> + ok. + +-spec to_module_info(binary()) -> list(). +to_module_info(Bin) -> + case catch(list_to_existing_atom(binary_to_list(Bin))) of + Mod -> Mod:module_info(); + _ -> [] + end. + +json_response(Info, Req, State) -> + Body = jsx:term_to_json(Info), + Headers = [{<<"Content-Type">>, <<"application/json">>}], + {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. From e58fe7f3424ec8c7599a32b650635bfec871af2b Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 16:54:07 +0100 Subject: [PATCH 029/405] Add additional example URLs --- README | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README b/README index c4251f3..289fd5f 100644 --- a/README +++ b/README @@ -15,11 +15,13 @@ Conceived for Spawnfest 2011, SMELLS LIKE BEAM SPIRIT. =========== -/vm lists running apps, releases, process count etc +/vm lists running apps, releases, process count etc -/rb/reports lists available SASL reports -/rb/reports/1 returns SASL report id 1 +/rb/reports lists available SASL reports +/rb/reports/1 returns SASL report id 1 -/pid/<1.2.3> process_info (POST to send msg to pid) -/module/NAME NAME:module_info() stuff +/pid/<1.2.3> process_info (POST to send msg to pid) +/pid/NAME as above, but using registered process name NAME +/pid/global/NAME as above, but using global registered process name NAME +/module/NAME NAME:module_info() stuff From d3673d8d1ac2b7559a1426f8708bf6667158d38b Mon Sep 17 00:00:00 2001 From: Steven Gravell Date: Sat, 9 Jul 2011 16:59:18 +0100 Subject: [PATCH 030/405] generic TPL --- apps/bigwig/priv/html/index.html | 4 +++- apps/bigwig/priv/js/tpl.js | 19 +++++++++++++------ apps/bigwig/src/bigwig_http_wh.erl | 6 ++++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/apps/bigwig/priv/html/index.html b/apps/bigwig/priv/html/index.html index ff86426..ebe51d3 100644 --- a/apps/bigwig/priv/html/index.html +++ b/apps/bigwig/priv/html/index.html @@ -13,9 +13,11 @@

- + diff --git a/apps/bigwig/priv/js/tpl.js b/apps/bigwig/priv/js/tpl.js index 6e03d31..c1a9a9a 100644 --- a/apps/bigwig/priv/js/tpl.js +++ b/apps/bigwig/priv/js/tpl.js @@ -1,14 +1,15 @@ -TPL = (function() { +var TPL = (function() { var title = function(title) { $('#title').text(title); } title("Loading..."); var host = document.location.host; - var sock = new WebSocket("ws://"+host+"/wh/ws"); - sock.onopen = function() { + var sock = null; + + var onopen = function() { } var visit = function(href) { sock.send(JSON.stringify({visit:href})); @@ -67,17 +68,23 @@ TPL = (function() { } } } - sock.onmessage = function(msg) { + var onmessage = function(msg) { var json = $.parseJSON(msg.data); console.log(json); for(var k in json) { update(k, json[k]); } } - sock.onclose = function() { + var onclose = function() { } return { - visit: visit + visit: visit, + connect: function(to) { + sock = new WebSocket("ws://"+host+to); + sock.onopen = onopen + sock.onmessage = onmessage + sock.onclose = onclose + } } })(); diff --git a/apps/bigwig/src/bigwig_http_wh.erl b/apps/bigwig/src/bigwig_http_wh.erl index 2fa2274..89ad029 100644 --- a/apps/bigwig/src/bigwig_http_wh.erl +++ b/apps/bigwig/src/bigwig_http_wh.erl @@ -23,6 +23,12 @@ websocket_init(_TransportName, Req, _Opts) -> {title, <<"What's Happening">>}, apps() ]), + %B = jsx:term_to_json([ + % {app, [ + % [{id, myapp},{name,"My Application"}, {description,"Description"}, {version,"Version"}] + % ]} + % ]), + %erlang:start_timer(1000, self(), B), {ok, Req, undefined_state}. websocket_handle({timeout, _Ref, Msg}, Req, State) -> From f9a8965dc9f3f124f76220e5b489f450f628823e Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 17:02:37 +0100 Subject: [PATCH 031/405] fix compile error --- apps/bigwig/src/bigwig_http_wh.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/bigwig/src/bigwig_http_wh.erl b/apps/bigwig/src/bigwig_http_wh.erl index 89ad029..cb2d967 100644 --- a/apps/bigwig/src/bigwig_http_wh.erl +++ b/apps/bigwig/src/bigwig_http_wh.erl @@ -13,10 +13,10 @@ init({tcp, http}, _Req, _Opts) -> {upgrade, protocol, cowboy_http_websocket}. handle(_Req, _State) -> - error(websockets_only). + exit(websockets_only). terminate(_Req, _State) -> - error(websockets_only). + exit(websockets_only). websocket_init(_TransportName, Req, _Opts) -> send([ From aebe98fc682010225600247c34ae05e6647b4722 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 17:04:21 +0100 Subject: [PATCH 032/405] Fix warning and avoid crashing on bad input --- apps/bigwig/src/bigwig_http_module.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/bigwig/src/bigwig_http_module.erl b/apps/bigwig/src/bigwig_http_module.erl index a3a3054..8c18da5 100644 --- a/apps/bigwig/src/bigwig_http_module.erl +++ b/apps/bigwig/src/bigwig_http_module.erl @@ -15,8 +15,8 @@ handle(Req0, State) -> handle_path('GET', [<<"module">>, Module], Req, State) -> case to_module_info(Module) of - [] -> not_found(Req, State); - Info -> json_response(Info, Req, State) + [_|_] = Info -> json_response(Info, Req, State); + _ -> not_found(Req, State) end; handle_path(_, _, Req, State) -> not_found(Req, State). @@ -31,8 +31,8 @@ terminate(_Req, _State) -> -spec to_module_info(binary()) -> list(). to_module_info(Bin) -> case catch(list_to_existing_atom(binary_to_list(Bin))) of - Mod -> Mod:module_info(); - _ -> [] + Mod when is_atom(Mod) -> catch(Mod:module_info()); + _ -> [] end. json_response(Info, Req, State) -> From 2ca4f5af4ac5770a6de71a6b42219e3c0467c76f Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 17:14:08 +0100 Subject: [PATCH 033/405] remove debug from error handler, wrap msgs in tagged module --- apps/bigwig/src/bigwig_error_handler.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/bigwig/src/bigwig_error_handler.erl b/apps/bigwig/src/bigwig_error_handler.erl index 51fb7a2..a85d820 100644 --- a/apps/bigwig/src/bigwig_error_handler.erl +++ b/apps/bigwig/src/bigwig_error_handler.erl @@ -27,8 +27,9 @@ register_client(Pid) -> init([]) -> {ok, #state{}}. handle_event(Event, State) -> - io:format("EVENT(to ~p) ~p\n", [length(State#state.listeners), Event]), - lists:foreach(fun(Pid) -> Pid ! Event end, State#state.listeners), + %%io:format("EVENT(to ~p) ~p\n", [length(State#state.listeners), Event]), + Msg = {?MODULE, Event}, + lists:foreach(fun(Pid) -> Pid ! Msg end, State#state.listeners), {ok, State}. handle_call({register_client, Pid}, State) -> From 9a51f855f6168531a58a37d9cdf13f05a844e9a9 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 17:15:15 +0100 Subject: [PATCH 034/405] implement /rb/stream - websockets endpoint that streams you SASL reports in realtime --- README | 1 + apps/bigwig/priv/html/websockets.html | 38 +++++++++++++++++++++++ apps/bigwig/src/bigwig_http.erl | 1 + apps/bigwig/src/bigwig_http_rb_stream.erl | 34 ++++++++++++++++++++ 4 files changed, 74 insertions(+) create mode 100644 apps/bigwig/priv/html/websockets.html create mode 100644 apps/bigwig/src/bigwig_http_rb_stream.erl diff --git a/README b/README index 289fd5f..022129f 100644 --- a/README +++ b/README @@ -19,6 +19,7 @@ Conceived for Spawnfest 2011, SMELLS LIKE BEAM SPIRIT. /rb/reports lists available SASL reports /rb/reports/1 returns SASL report id 1 +/rb/stream websocket endpoint for streaming new SASL reports as JSON /pid/<1.2.3> process_info (POST to send msg to pid) /pid/NAME as above, but using registered process name NAME diff --git a/apps/bigwig/priv/html/websockets.html b/apps/bigwig/priv/html/websockets.html new file mode 100644 index 0000000..0d1a04a --- /dev/null +++ b/apps/bigwig/priv/html/websockets.html @@ -0,0 +1,38 @@ + + + + +

Mochiweb websocket demo

+ +
+ +   State: +
+
Protip: open your javascript error console, just in case..
+
+
+ + +
+
+
+ + + + + diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl index 3218310..75a7576 100644 --- a/apps/bigwig/src/bigwig_http.erl +++ b/apps/bigwig/src/bigwig_http.erl @@ -29,6 +29,7 @@ dispatch_rules() -> , {[<<"wh">>], bigwig_http_static, ["html","index.html"]} , {[<<"wh">>, <<"ws">>], bigwig_http_wh, []} , {[<<"vm">>], bigwig_http_vm, []} + , {[<<"rb">>, <<"stream">>], bigwig_http_rb_stream, []} , {[<<"rb">>, '...'], bigwig_http_rb, []} , {[<<"pid">>, '...'], bigwig_http_pid, []} , {[<<"module">>, '...'], bigwig_http_module, []} diff --git a/apps/bigwig/src/bigwig_http_rb_stream.erl b/apps/bigwig/src/bigwig_http_rb_stream.erl new file mode 100644 index 0000000..84cf695 --- /dev/null +++ b/apps/bigwig/src/bigwig_http_rb_stream.erl @@ -0,0 +1,34 @@ +%% +%% report browser streaming of new SASL reports via websocket +%% +-module(bigwig_http_rb_stream). +-behaviour(cowboy_http_handler). +-behaviour(cowboy_http_websocket_handler). +-export([init/3, handle/2, terminate/2]). +-export([websocket_init/3, websocket_handle/3, websocket_terminate/3]). + +init({tcp, http}, _Req, _Opts) -> + {upgrade, protocol, cowboy_http_websocket}. + +handle(Req, State) -> + {ok, Req2} = cowboy_http_req:reply(404, [], <<"Websockets only here pls">>, Req), + {ok, Req2, State}. + +terminate(_Req, _State) -> + ok. + + +websocket_init(_TransportName, Req, _Opts) -> + bigwig_error_handler:register_client(self()), + {ok, Req, undefined_state}. + +websocket_handle({bigwig_error_handler, Report}, Req, State) -> + Msg = jsx:term_to_json([{report, Report}]), + {reply, Msg, Req, State}; + +websocket_handle({websocket, Msg}, Req, State) -> + {reply, << "You said: ", Msg/binary >>, Req, State}. + +websocket_terminate(_Reason, _Req, _State) -> + ok. + From 97cfd08524de96d0c0430347c11111c9982ec42a Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Sat, 9 Jul 2011 17:23:34 +0100 Subject: [PATCH 035/405] switching nodes working --- apps/bigwig/include/etop_defs.hrl | 2 +- apps/bigwig/src/etop2.erl | 148 +++++++++++++++++------------- 2 files changed, 84 insertions(+), 66 deletions(-) diff --git a/apps/bigwig/include/etop_defs.hrl b/apps/bigwig/include/etop_defs.hrl index f784554..0ea42e4 100644 --- a/apps/bigwig/include/etop_defs.hrl +++ b/apps/bigwig/include/etop_defs.hrl @@ -25,5 +25,5 @@ -record(opts, {node=node(), port = 8415, accum = false, intv = 5000, lines = 10, width = 700, height = 340, sort = runtime, tracing = on, %% Other state information - host, tracer, store, + out_mod=etop_gui, out_proc, server, host, tracer, store, accum_tab, remote}). diff --git a/apps/bigwig/src/etop2.erl b/apps/bigwig/src/etop2.erl index c18bf8d..fd48061 100644 --- a/apps/bigwig/src/etop2.erl +++ b/apps/bigwig/src/etop2.erl @@ -22,7 +22,7 @@ -behaviour(gen_server). %% API --export([start_link/0, start_link/1, update/0]). +-export([start_link/0, start_link/1, update/0, node/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). @@ -37,9 +37,10 @@ -define(SERVER, etop2_server). -%% Usage of the erlang top program -%% Options are set as command line parameters as in -node a@host -.. -%% or as parameter to etop:start([{node, a@host}, {...}]) +-define(ACCUM_TAB, accum_tab). + +%% Options are set as parameters to etop2:start_link([{node, a@host}, {...}]) +%% %% Options are: %% node atom Required The erlang node to measure %% port integer The used port, NOTE: due to a bug this program @@ -60,60 +61,27 @@ %% same as the cookie for the measured node. %% This is not an etop parameter -terminate(_Reason, State=#opts{tracing=on}) -> - etop_tr:stop_tracer(State); -terminate(_Reason, _) -> - ok. - start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). start_link(Opts) -> gen_server:start_link({local, ?SERVER}, ?MODULE, [Opts], []). -handle_cast(_Msg, State) -> - {stop, unhandled_cast, State}. -handle_info(_Info, State) -> - {stop, unhandled_info, State}. -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -init(Opts) -> - Config1 = #opts{node=Node} = handle_args(Opts, #opts{}), - - %% Connect to the node we want to look at - %% TODO: Move the connect elsewhere so we can switch nodes - case connect(Node) of - ok -> - %% Maybe set up the tracing - Config2 = setup_tracing(Config1), - - AccumTab = ets:new(accum_tab, - [set,public,{keypos,#etop_proc_info.pid}]), - Config3 = Config2#opts{accum_tab=AccumTab}, - - {ok, Config3}; - Err -> - Err - end. - -check_runtime_tools_vsn(Node) -> - case rpc:call(Node,observer_backend,vsn,[]) of - {ok, Vsn} -> check_vsn(Vsn); - _ -> {error, "faulty version of runtime_tools on remote node"} - end. -check_vsn(_Vsn) -> ok. - -%% Handle the incoming data - update() -> gen_server:call(?SERVER, update). -check_runtime_config(lines,L) when is_integer(L),L>0 -> ok; -check_runtime_config(sort,S) - when S=:=runtime; S=:=reductions; S=:=memory; S=:=msg_q -> ok; -check_runtime_config(accumulate,A) when A=:=true; A=:=false -> ok; -check_runtime_config(_Key,_Value) -> error. +node(Node) -> + gen_server:call(?SERVER, {node, Node}). + +init(Opts) -> + Config1 = handle_args(Opts, #opts{}), + AccumTab = ets:new(?ACCUM_TAB, [set,public,{keypos,#etop_proc_info.pid}]), + Config2 = Config1#opts{accum_tab=AccumTab}, + Res = connect(Config2), + Res. + +handle_cast(_Msg, State) -> + {stop, unhandled_cast, State}. handle_call(update, _From, State) -> Info = update(State), @@ -126,7 +94,38 @@ handle_call({config, Key, Value}, _From, State) -> _ -> State2 = putopt(Key, Value, State), {reply, ok, State2} + end; +handle_call({node, Node}, _From, State=#opts{node=Node}) -> + {reply, ok, State}; +handle_call({node, Node}, _From, State) -> + State2 = stop_tracing(State), + clear_ets(State2), + case connect(State2#opts{node=Node}) of + {ok, State3} -> + {reply, ok, State3}; + {error, Error} -> + {stop, Error, State2} + end. + +handle_info(_Info, State) -> + {stop, unhandled_info, State}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +terminate(_Reason, State=#opts{tracing=on}) -> + etop_tr:stop_tracer(State); +terminate(_Reason, _) -> + ok. + +%% Internal functions + +check_runtime_tools_vsn(Node) -> + case rpc:call(Node,observer_backend,vsn,[]) of + {ok, Vsn} -> check_vsn(Vsn); + _ -> {error, "faulty version of runtime_tools on remote node"} end. +check_vsn(_Vsn) -> ok. update(#opts{store=Store,node=Node,tracing=Tracing}=Opts) -> Pid = spawn_link(Node,observer_backend,etop_collect,[self()]), @@ -184,7 +183,6 @@ get_tag(memory) -> #etop_proc_info.mem; get_tag(reductions) -> #etop_proc_info.reds; get_tag(msg_q) -> #etop_proc_info.mq. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Configuration Management @@ -255,21 +253,22 @@ get_mem(Tag, MemI) -> _ -> 0 end. -update_json(Info, Config) -> - {Cpu,NProcs,RQ,Clock} = loadinfo(Info), +update_json(Info, #opts{node=Node}) -> + {Cpu,NProcs,RQ,Clock0} = loadinfo(Info), + Clock = iolist_to_binary(Clock0), Header = case Info#etop_info.memi of undefined -> - [{<<"node">>, Config#opts.node}, - {<<"clock">>, iolist_to_binary(Clock)}, + [{<<"node">>, Node}, + {<<"clock">>, Clock}, {<<"cpu">>, Cpu}, {<<"nprocs">>, NProcs}, {<<"runqueue">>, RQ}]; Memi -> [Tot,Procs,Atom,Bin,Code,Ets] = etop2:meminfo(Memi, [total,processes,atom,binary,code,ets]), - [{<<"node">>, Config#opts.node}, - {<<"clock">>, iolist_to_binary(Clock)}, + [{<<"node">>, Node}, + {<<"clock">>, Clock}, {<<"cpu">>, Cpu}, {<<"tot">>, Tot}, {<<"bin">>, Bin}, @@ -284,11 +283,8 @@ update_json(Info, Config) -> [{<<"header">>, Header}, {<<"procs">>, Ps}]. -formatmfa({M, F, A}) -> - io_lib:format("~w:~w/~w",[M, F, A]). - -to_list(Name) when is_atom(Name) -> atom_to_list(Name); -to_list({_M,_F,_A}=MFA) -> formatmfa(MFA). +name(Name) when is_atom(Name) -> Name; +name({M,F,A}) -> [M, F, A]. etop_proc_info_to_json( #etop_proc_info{pid=Pid, @@ -300,22 +296,28 @@ etop_proc_info_to_json( mq=MQ}) -> PInfo = [{<<"pid">>, Pid}, - {<<"name">>, to_list(Name)}, + {<<"name">>, name(Name)}, {<<"time">>, Time}, {<<"reds">>, Reds}, {<<"mem">>, Mem}, {<<"mq">>, MQ}, - {<<"mfa">>, formatmfa(MFA)}], + {<<"mfa">>, name(MFA)}], {<<"pinfo">>, PInfo}. -connect(Node) -> +%% Connect to a node and potentially set up tracing +-spec connect(#opts{}) -> {ok, #opts{}} | {error, any()}. +connect(State=#opts{node=Node}) -> case net_adm:ping(Node) of pang when Node /= node() -> {error, "node connect failed"}; _Pong -> - check_runtime_tools_vsn(Node) + case check_runtime_tools_vsn(Node) of + ok -> {ok, setup_tracing(State)}; + {error, _} = E -> E + end end. +-spec setup_tracing(#opts{}) -> #opts{}. setup_tracing(Config=#opts{tracing=T, sort=S, node=N}) -> if T =:= on, N /= node() -> %% Cannot trace on current node since the tracer will @@ -328,3 +330,19 @@ setup_tracing(Config=#opts{tracing=T, sort=S, node=N}) -> Config#opts{tracing=off} end end. + +-spec stop_tracing(#opts{}) -> #opts{}. +stop_tracing(State=#opts{tracing=on}) -> + etop_tr:stop_tracer(State); +stop_tracing(State) -> + State. + +-spec clear_ets(#opts{}) -> ok | {error, any()}. +clear_ets(#opts{accum_tab=AccumTab}) -> + ets:delete_all_objects(AccumTab). + +check_runtime_config(lines,L) when is_integer(L),L>0 -> ok; +check_runtime_config(sort,S) + when S=:=runtime; S=:=reductions; S=:=memory; S=:=msg_q -> ok; +check_runtime_config(accumulate,A) when A=:=true; A=:=false -> ok; +check_runtime_config(_Key,_Value) -> error. From 5743616f06d1d879fb3835ffa5661254f65699da Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Sat, 9 Jul 2011 17:30:36 +0100 Subject: [PATCH 036/405] Config -> State --- apps/bigwig/src/etop2.erl | 66 +++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/apps/bigwig/src/etop2.erl b/apps/bigwig/src/etop2.erl index fd48061..cf161cd 100644 --- a/apps/bigwig/src/etop2.erl +++ b/apps/bigwig/src/etop2.erl @@ -74,10 +74,10 @@ node(Node) -> gen_server:call(?SERVER, {node, Node}). init(Opts) -> - Config1 = handle_args(Opts, #opts{}), + State1 = handle_args(Opts, #opts{}), AccumTab = ets:new(?ACCUM_TAB, [set,public,{keypos,#etop_proc_info.pid}]), - Config2 = Config1#opts{accum_tab=AccumTab}, - Res = connect(Config2), + State2 = State1#opts{accum_tab=AccumTab}, + Res = connect(State2), Res. handle_cast(_Msg, State) -> @@ -186,45 +186,45 @@ get_tag(msg_q) -> #etop_proc_info.mq. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Configuration Management -putopt(Key, Value, #opts{} = Config) -> - handle_args([{Key, Value}], Config). +putopt(Key, Value, #opts{} = State) -> + handle_args([{Key, Value}], State). -handle_args([{node, [NodeString]}| R], Config) when is_list(NodeString) -> +handle_args([{node, [NodeString]}| R], State) when is_list(NodeString) -> Node = list_to_atom(NodeString), - NewC = Config#opts{node = Node}, + NewC = State#opts{node = Node}, handle_args(R, NewC); -handle_args([{node, Node} |R], Config) when is_atom(Node) -> - NewC = Config#opts{node = Node}, +handle_args([{node, Node} |R], State) when is_atom(Node) -> + NewC = State#opts{node = Node}, handle_args(R, NewC); -handle_args([{port, Port}| R], Config) when is_integer(Port) -> - NewC = Config#opts{port=Port}, +handle_args([{port, Port}| R], State) when is_integer(Port) -> + NewC = State#opts{port=Port}, handle_args(R, NewC); -handle_args([{port, [Port]}| R], Config) when is_list(Port) -> - NewC = Config#opts{port= list_to_integer(Port)}, +handle_args([{port, [Port]}| R], State) when is_list(Port) -> + NewC = State#opts{port= list_to_integer(Port)}, handle_args(R, NewC); -handle_args([{lines, Lines}| R], Config) when is_integer(Lines) -> - NewC = Config#opts{lines=Lines}, +handle_args([{lines, Lines}| R], State) when is_integer(Lines) -> + NewC = State#opts{lines=Lines}, handle_args(R, NewC); -handle_args([{lines, [Lines]}| R], Config) when is_list(Lines) -> - NewC = Config#opts{lines= list_to_integer(Lines)}, +handle_args([{lines, [Lines]}| R], State) when is_list(Lines) -> + NewC = State#opts{lines= list_to_integer(Lines)}, handle_args(R, NewC); -handle_args([{accumulate, Bool}| R], Config) when is_atom(Bool) -> - NewC = Config#opts{accum=Bool}, +handle_args([{accumulate, Bool}| R], State) when is_atom(Bool) -> + NewC = State#opts{accum=Bool}, handle_args(R, NewC); -handle_args([{accumulate, [Bool]}| R], Config) when is_list(Bool) -> - NewC = Config#opts{accum= list_to_atom(Bool)}, +handle_args([{accumulate, [Bool]}| R], State) when is_list(Bool) -> + NewC = State#opts{accum= list_to_atom(Bool)}, handle_args(R, NewC); -handle_args([{sort, Sort}| R], Config) when is_atom(Sort) -> - NewC = Config#opts{sort=Sort}, +handle_args([{sort, Sort}| R], State) when is_atom(Sort) -> + NewC = State#opts{sort=Sort}, handle_args(R, NewC); -handle_args([{sort, [Sort]}| R], Config) when is_list(Sort) -> - NewC = Config#opts{sort= list_to_atom(Sort)}, +handle_args([{sort, [Sort]}| R], State) when is_list(Sort) -> + NewC = State#opts{sort= list_to_atom(Sort)}, handle_args(R, NewC); -handle_args([{tracing, OnOff}| R], Config) when is_atom(OnOff) -> - NewC = Config#opts{tracing=OnOff}, +handle_args([{tracing, OnOff}| R], State) when is_atom(OnOff) -> + NewC = State#opts{tracing=OnOff}, handle_args(R, NewC); -handle_args([{tracing, [OnOff]}| R], Config) when is_list(OnOff) -> - NewC = Config#opts{tracing=list_to_atom(OnOff)}, +handle_args([{tracing, [OnOff]}| R], State) when is_list(OnOff) -> + NewC = State#opts{tracing=list_to_atom(OnOff)}, handle_args(R, NewC); handle_args([_| R], C) -> @@ -318,16 +318,16 @@ connect(State=#opts{node=Node}) -> end. -spec setup_tracing(#opts{}) -> #opts{}. -setup_tracing(Config=#opts{tracing=T, sort=S, node=N}) -> +setup_tracing(State=#opts{tracing=T, sort=S, node=N}) -> if T =:= on, N /= node() -> %% Cannot trace on current node since the tracer will %% trace itself - etop_tr:setup_tracer(Config); + etop_tr:setup_tracer(State); true -> if S =:= runtime -> - Config#opts{sort=reductions, tracing=off}; + State#opts{sort=reductions, tracing=off}; true -> - Config#opts{tracing=off} + State#opts{tracing=off} end end. From 8c059cac137b11fafce2eba0c7299399172d8421 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Sat, 9 Jul 2011 17:36:55 +0100 Subject: [PATCH 037/405] add etop2 to bigwig supervision tree --- apps/bigwig/src/bigwig_sup.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/bigwig/src/bigwig_sup.erl b/apps/bigwig/src/bigwig_sup.erl index 54cfe78..0c78053 100644 --- a/apps/bigwig/src/bigwig_sup.erl +++ b/apps/bigwig/src/bigwig_sup.erl @@ -25,8 +25,9 @@ start_link() -> init([]) -> Http = ?CHILD(bigwig_http, worker), ErrMon = ?CHILD(bigwig_error_handler_mon, worker), + Etop = ?CHILD(etop2, worker), - Specs = [ ErrMon, Http ], + Specs = [ ErrMon, Http, Etop ], {ok, { {one_for_one, 5, 10}, Specs} }. From 0b6e320018d67111d25b87d83db5af417ff565f8 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 17:39:24 +0100 Subject: [PATCH 038/405] use application env for cowboy config, reduce default acceptors to 16 --- apps/bigwig/src/bigwig_http.erl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl index 75a7576..7c62564 100644 --- a/apps/bigwig/src/bigwig_http.erl +++ b/apps/bigwig/src/bigwig_http.erl @@ -37,12 +37,22 @@ dispatch_rules() -> , {'_', bigwig_http_catchall, []} ]}]. +confval(Key, Default) -> + case application:get_env(Key) of + undefined -> Default; + Val -> Val + end. + init([]) -> - Port = 8080, - error_logger:info_msg("Starting http server on port ~p", [Port]), + Port = confval(port, 8080), + Ip = confval(ip, "127.0.0.1"), + NumAcceptors = confval(num_acceptors, 16), + + IpStr = case is_list(Ip) of true -> Ip; false -> inet_parse:ntoa(Ip) end, + error_logger:info_msg("Bigwig listening on http://~s:~B/~n", [IpStr,Port]), %% %% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts - cowboy:start_listener(http, 100, + cowboy:start_listener(http, NumAcceptors, cowboy_tcp_transport, [{port, Port}], cowboy_http_protocol, [{dispatch, dispatch_rules()}] ), From f55c5dea742d93eb7cbb1dfea6b3ff95f381fd37 Mon Sep 17 00:00:00 2001 From: Steven Gravell Date: Sat, 9 Jul 2011 17:51:44 +0100 Subject: [PATCH 039/405] change /rb to be the streaming rb index page --- apps/bigwig/priv/html/rb.html | 23 +++++++++++++++++++++++ apps/bigwig/priv/js/tpl.js | 23 ++++++++++++++++++++--- apps/bigwig/src/bigwig_http.erl | 1 + apps/bigwig/src/bigwig_http_rb_stream.erl | 12 +++++++++++- start-dev.sh | 1 + 5 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 apps/bigwig/priv/html/rb.html diff --git a/apps/bigwig/priv/html/rb.html b/apps/bigwig/priv/html/rb.html new file mode 100644 index 0000000..ff85a85 --- /dev/null +++ b/apps/bigwig/priv/html/rb.html @@ -0,0 +1,23 @@ + + + So Peter... what's happening? + + + +

Reports

+
    +
  • + + + + +
  • +
+ + + + + + diff --git a/apps/bigwig/priv/js/tpl.js b/apps/bigwig/priv/js/tpl.js index c1a9a9a..52eae73 100644 --- a/apps/bigwig/priv/js/tpl.js +++ b/apps/bigwig/priv/js/tpl.js @@ -21,14 +21,30 @@ var TPL = (function() { return false; } } + var str = function(v) { + if(type(v) == "pid") { + return v.data; + } + return v; + } var updateChildren = function(el, data) { for(var k in data) { - el.data(k, data[k]); + var v = str(data[k]); + el.data(k, v); var child = $("."+k, el); if(child) { - $("."+k, el).text(data[k]); + $("."+k, el).text(v); + } + } + } + var type = function(v) { + var t = typeof v; + if(t == "object") { + if(v['_type'] != undefined) { + t = v['_type']; } } + return t; } var li = function(type, p, data) { var id = data.id; @@ -55,7 +71,8 @@ var TPL = (function() { if(el.length==0) { el = $('*[data-id='+k+']').first(); } - var jt = typeof data; + var jt = type(data); + if(jt == "string") { el.text(data); } else if(jt == "object") { diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl index 7c62564..7b476f0 100644 --- a/apps/bigwig/src/bigwig_http.erl +++ b/apps/bigwig/src/bigwig_http.erl @@ -29,6 +29,7 @@ dispatch_rules() -> , {[<<"wh">>], bigwig_http_static, ["html","index.html"]} , {[<<"wh">>, <<"ws">>], bigwig_http_wh, []} , {[<<"vm">>], bigwig_http_vm, []} + , {[<<"rb">>], bigwig_http_static, ["html","rb.html"]} , {[<<"rb">>, <<"stream">>], bigwig_http_rb_stream, []} , {[<<"rb">>, '...'], bigwig_http_rb, []} , {[<<"pid">>, '...'], bigwig_http_pid, []} diff --git a/apps/bigwig/src/bigwig_http_rb_stream.erl b/apps/bigwig/src/bigwig_http_rb_stream.erl index 84cf695..3f2e1fe 100644 --- a/apps/bigwig/src/bigwig_http_rb_stream.erl +++ b/apps/bigwig/src/bigwig_http_rb_stream.erl @@ -23,7 +23,17 @@ websocket_init(_TransportName, Req, _Opts) -> {ok, Req, undefined_state}. websocket_handle({bigwig_error_handler, Report}, Req, State) -> - Msg = jsx:term_to_json([{report, Report}]), + io:format("Report ~p~n", [Report]), + {Level, Leader, {Pid, Type, _Data}} = Report, + Msg = jsx:term_to_json([{report, [ + [ + {level,Level}, + {group_leader,Leader}, + {pid, Pid}, + {type, Type}%, + %{data, Data} + ] + ]}]), {reply, Msg, Req, State}; websocket_handle({websocket, Msg}, Req, State) -> diff --git a/start-dev.sh b/start-dev.sh index 6c0429f..055575b 100755 --- a/start-dev.sh +++ b/start-dev.sh @@ -1,4 +1,5 @@ #!/bin/sh cd `dirname $0` make +mkdir -p log/sasl exec erl -sname bigwig -pa $PWD/apps/*/ebin $PWD/deps/*/ebin -boot start_sasl -config sys.config -s reloader -s bigwig From c97bcf0ba13f17b221eddac869064ede2c3e5cf1 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 18:14:55 +0100 Subject: [PATCH 040/405] Change lists:remove/2 to lists:delete/2 --- apps/bigwig/src/bigwig_error_handler.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/bigwig/src/bigwig_error_handler.erl b/apps/bigwig/src/bigwig_error_handler.erl index a85d820..5ff5082 100644 --- a/apps/bigwig/src/bigwig_error_handler.erl +++ b/apps/bigwig/src/bigwig_error_handler.erl @@ -40,7 +40,7 @@ handle_call({register_client, Pid}, State) -> handle_call(_Msg, State) -> {ok, not_implemented, State}. handle_info({'DOWN', _MonitorRef, process, Pid, _Reason}, State) -> - NewListeners = lists:remove(Pid, State#state.listeners), + NewListeners = lists:delete(Pid, State#state.listeners), {ok, ok, State#state{ listeners=NewListeners }}; handle_info(_Info, State) -> {ok, State}. From e97f8233ecd544942843be57cb0752ebb671fa64 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Sat, 9 Jul 2011 18:21:58 +0100 Subject: [PATCH 041/405] Fix typo --- apps/bigwig/src/bigwig.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/bigwig/src/bigwig.app.src b/apps/bigwig/src/bigwig.app.src index abbaa05..12e484c 100644 --- a/apps/bigwig/src/bigwig.app.src +++ b/apps/bigwig/src/bigwig.app.src @@ -1,6 +1,6 @@ {application, bigwig, [ - {description, "web-based suite of tools for interrogating and erlang system"}, + {description, "web-based suite of tools for interrogating an erlang system"}, {vsn, "0.1"}, {registered, []}, {applications, [ From 6c344ddfd0de02a86d317a5ba5f7b4aac208d6e5 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 9 Jul 2011 18:23:10 +0100 Subject: [PATCH 042/405] fix binary pathing stuff for rb --- apps/bigwig/src/bigwig_http.erl | 4 ++-- apps/bigwig/src/bigwig_http_static.erl | 19 +++++-------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/apps/bigwig/src/bigwig_http.erl b/apps/bigwig/src/bigwig_http.erl index 7b476f0..142433b 100644 --- a/apps/bigwig/src/bigwig_http.erl +++ b/apps/bigwig/src/bigwig_http.erl @@ -26,10 +26,10 @@ dispatch_rules() -> {[<<"">>], bigwig_http_index, []} , {[<<"static">>, '...'], bigwig_http_static, []} - , {[<<"wh">>], bigwig_http_static, ["html","index.html"]} + , {[<<"wh">>], bigwig_http_static, [<<"html">>,<<"index.html">>]} , {[<<"wh">>, <<"ws">>], bigwig_http_wh, []} , {[<<"vm">>], bigwig_http_vm, []} - , {[<<"rb">>], bigwig_http_static, ["html","rb.html"]} + , {[<<"rb">>], bigwig_http_static, [<<"html">>,<<"rb.html">>]} , {[<<"rb">>, <<"stream">>], bigwig_http_rb_stream, []} , {[<<"rb">>, '...'], bigwig_http_rb, []} , {[<<"pid">>, '...'], bigwig_http_pid, []} diff --git a/apps/bigwig/src/bigwig_http_static.erl b/apps/bigwig/src/bigwig_http_static.erl index 27de59e..7a112ac 100644 --- a/apps/bigwig/src/bigwig_http_static.erl +++ b/apps/bigwig/src/bigwig_http_static.erl @@ -15,14 +15,15 @@ init({tcp, http}, Req, OnlyFile) -> {ok, Req, OnlyFile}. handle(Req, undefined_state = State) -> - {[_|Path], _} = cowboy_http_req:path(Req), % strip <<"static">> - send(Req, Path, State); + {[_|Path], Req2} = cowboy_http_req:path(Req), % strip <<"static">> + send(Req2, Path, State); handle(Req, OnlyFile = State) -> send(Req, OnlyFile, State). -send(Req, Path, State) -> +send(Req, PathBins, State) -> Headers = [{<<"Content-Type">>, <<"text/html">>}], + Path = [ binary_to_list(P) || P <- PathBins ], {ok, Body} = file(filename:join(Path)), {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), {ok, Req2, State}. @@ -42,17 +43,7 @@ type(Type, Name) -> file(Path) -> Priv = priv(), - file:read_file(check(Priv, filename:join(Priv, Path))). + file:read_file(filename:join(Priv, Path)). priv() -> code:priv_dir(bigwig). - -check(Priv, Path) when is_list(Priv) orelse is_list(Path) -> - check(iolist_to_binary(Priv), iolist_to_binary(Path)); -check(Priv, Path) -> - Size = byte_size(Priv), - <> = Path, - case CheckPriv of - Priv -> Path; - _ -> throw({unauthorized, Path}) - end. From 2bcfcba3802bd369ba24cf86a2782e8eb9063f8f Mon Sep 17 00:00:00 2001 From: Steven Gravell Date: Sat, 9 Jul 2011 19:16:01 +0100 Subject: [PATCH 043/405] push last 50 on init --- apps/bigwig/priv/html/rb.html | 2 +- apps/bigwig/priv/js/tpl.js | 6 +- apps/bigwig/src/bigwig_http_rb_stream.erl | 74 +++++++++++++++++------ apps/bigwig/src/bigwig_http_wh.erl | 5 +- 4 files changed, 64 insertions(+), 23 deletions(-) diff --git a/apps/bigwig/priv/html/rb.html b/apps/bigwig/priv/html/rb.html index ff85a85..c4f438f 100644 --- a/apps/bigwig/priv/html/rb.html +++ b/apps/bigwig/priv/html/rb.html @@ -5,7 +5,7 @@

Reports

-
  • - + @@ -169,11 +169,11 @@

    BigWig

    - + - + diff --git a/priv/js/rb.js b/priv/js/rb.js index 8f60a80..d6b1bf3 100644 --- a/priv/js/rb.js +++ b/priv/js/rb.js @@ -28,9 +28,10 @@ RB = (function() { break; } } - //el.click(function() { - // el.toggleClass('selected'); - //}); + $('.report_level', el).click(function() { + el.toggleClass('selected'); + return false; + }); if(data && data.started) { el.addClass('started'); @@ -39,7 +40,7 @@ RB = (function() { el.addClass('application_start'); TPL.update($('.application_start',el), fmt_application_start(data)); } else { - el.addClass('selected'); + //el.addClass('selected'); } if(data) { $('.data', el).text(JSON.stringify(data, null, 4)); @@ -48,9 +49,6 @@ RB = (function() { } }); - $('#stats').bind('onupdate', function(e, data) { - console.log("stats", data); - }); $('.filter').click(function() { var href = $(this).attr('href'); diff --git a/priv/js/tpl.js b/priv/js/tpl.js index c5a0af1..35b8620 100644 --- a/priv/js/tpl.js +++ b/priv/js/tpl.js @@ -41,7 +41,7 @@ var TPL = (function() { } } - var str = function(v) { + var render = function(v) { var t = type(v); if(t == "pid") { return RENDERER.json(v); @@ -55,12 +55,15 @@ var TPL = (function() { var updateChildren = function(el, data) { for(var k in data) { - var v = str(data[k]); + var v = render(data[k]); el.data(k, v); var child = $("."+k, el); - if(child) { - $("."+k, el).children().remove(); - $("."+k, el).append(v); + if(child.length > 0 && v) { + child.children().remove(); + try { + child.append(v); + } catch(err) { + } } } } @@ -148,7 +151,10 @@ var TPL = (function() { if(jt == "string") { el.children().remove(); - el.append(data); + try { + el.append(data); + } catch(err) { + } } else if(jt == "object") { if(data[0]) { for(var i=0; i Date: Sun, 10 Jul 2011 21:35:31 +0100 Subject: [PATCH 136/405] remove debug dialog link --- priv/html/index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index 7a30983..1e3313a 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -35,7 +35,6 @@

    BigWig

-DIALOG
Node Stats
From 1369dd6c78f9e15b064588f0c211c9bedba0814b Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sun, 10 Jul 2011 21:51:37 +0100 Subject: [PATCH 137/405] discard non-stats msgs --- src/bigwig_http_stats_stream.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index 6e2201a..def02ac 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -26,6 +26,9 @@ websocket_handle({bigwig, {bigwig_stats_sender, Stats}}, Req, State) -> Reply = jsx:term_to_json([{stats, Stats}]), {reply, Reply, Req, State}; +websocket_handle({bigwig, _}, Req, State) -> + {ok, Req, State}; + websocket_handle(Msg, Req, State) -> io:format("Unhandled msg to ~p ~p\n", [?MODULE, Msg]), {ok, Req, State}. From 5da6f76913e2b98c2ce3cfc91bf39a2040bf467d Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sun, 10 Jul 2011 22:14:42 +0100 Subject: [PATCH 138/405] add kill/send buttons to pid dialogs --- priv/js/renderer.js | 32 +++++++++++++++++++++++++++++++- src/bigwig_http_pid.erl | 11 +++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/priv/js/renderer.js b/priv/js/renderer.js index c4c5191..cc6bc52 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -11,7 +11,37 @@ var RENDERER = (function() { title: 'process_info(' + Pid + ')   ' + '(json)', width: 460, - buttons: { "Close": function() { $(this).dialog("close"); } } + buttons: { + "Close": function() { $(this).dialog("close"); }, + "Send Msg": function() { + var msg = prompt("Enter Erlang term to send to <" + Pid + ">"); + if(msg) + { + var thisdiag = $(this); + $.ajax({ + url:url, + type:'POST', + data: {msg: msg}, + success: function(resp){ + alert('Sent ok'); + } + }) + } + }, + "Kill" : function() { + if(confirm("Sure you want to kill <" + Pid + ">")) + { + var thisdiag = $(this); + $.ajax({ + url:url, + type:'DELETE', + success: function(resp){ + thisdiag.dialog("close"); + } + }) + } + } + } }); } }); diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index b16f6aa..2440611 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -21,6 +21,8 @@ handle_path('POST', [<<"pid">>, <<"global">>, Name], Req, State) -> handle_post_pid(fun to_global_pid/1, Name, Req, State); handle_path('POST', [<<"pid">>, Pid], Req, State) -> handle_post_pid(fun to_pid/1, Pid, Req, State); +handle_path('DELETE', [<<"pid">>, Pid], Req, State) -> + handle_delete_pid(fun to_pid/1, Pid, Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). @@ -44,6 +46,15 @@ handle_post_pid(Get, Pid0, Req, State) -> _ -> not_found(Req, State) end. +handle_delete_pid(Get, Pid0, Req, State) -> + case catch(Get(Pid0)) of + Pid when is_pid(Pid) -> + erlang:exit(Pid, kill), + {ok, Req2} = cowboy_http_req:reply(200, [], "killed", Req), + {ok, Req2, State}; + _ -> not_found(Req, State) + end. + post_pid_response(Pid, Req, State) -> {Res, Req1} = cowboy_http_req:body_qs(Req), Headers = [{<<"Content-Type">>, <<"application/x-erlang-term">>}], From 3279891fccd056e7629276c6df1f5aa63a2828a0 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Sun, 10 Jul 2011 22:23:05 +0100 Subject: [PATCH 139/405] Only apply RENDERER.render_json_val to known pid and module-ish columns. Sort by reds by default on start. --- priv/js/et.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/priv/js/et.js b/priv/js/et.js index 1a6a306..c24e09d 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -3,6 +3,7 @@ $(document).ready(function() { "bProcessing": false, "iDisplayLength": 25, "sAjaxSource": '/top', + "aaSorting": [[5, "desc"]], // Get data piggy-backed on the main table and update the page "fnServerData": function(sSource, aaData, fnCallback) { $.getJSON( sSource, aaData, function(json) { @@ -11,10 +12,9 @@ $(document).ready(function() { // Use our RENDERER to format everything for(var i=0; i Date: Sun, 10 Jul 2011 22:26:51 +0100 Subject: [PATCH 140/405] "Blow through nose, snort" --- priv/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index 1e3313a..223f340 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -9,7 +9,7 @@
@@ -152,10 +152,10 @@

BigWig

Started: - - + +
- +

             
From 615e679e54c7d696f947eeca8e6ba7034c9c26b0 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Mon, 11 Jul 2011 00:39:22 +0100 Subject: [PATCH 162/405] Add Dashboard tool --- priv/html/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/html/index.html b/priv/html/index.html index 1a540da..a4de6bf 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -13,6 +13,7 @@

BigWig

    +
  • Dashboard
  • Reports
  • Processes
  • Applications From b4bb30ca39c7acb2bca43cba7f9f9037ded97528 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Mon, 11 Jul 2011 00:41:33 +0100 Subject: [PATCH 163/405] Activate dashboard by default --- priv/html/index.html | 3 +++ priv/js/bigwig.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index a4de6bf..ec97598 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -102,6 +102,9 @@

    BigWig

+
+
+
diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index f41f8ce..e34fb39 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -20,7 +20,7 @@ BigWig = (function() { //TPL.update('loaded', data.loaded); }); TPL.fetch("/vm"); - activate('etop'); + activate('dashboard'); })(); return { From 1ee8da08f742b3cb1cee997efdc7760ee6c14cf6 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Mon, 11 Jul 2011 00:47:25 +0100 Subject: [PATCH 164/405] Fetch application info into dashboard --- priv/html/index.html | 35 +++++++++++++++++------------------ priv/js/bigwig.js | 4 +++- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index ec97598..8db17d9 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -16,24 +16,7 @@

BigWig

  • Dashboard
  • Reports
  • Processes
  • -
  • Applications -
    - - -
    -
  • +
  • Applications
  • @@ -103,6 +86,22 @@

    BigWig

    +
    + + +
    diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index e34fb39..ecc2bf8 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -19,7 +19,6 @@ BigWig = (function() { TPL.update('running', data.running); //TPL.update('loaded', data.loaded); }); - TPL.fetch("/vm"); activate('dashboard'); })(); @@ -28,3 +27,6 @@ BigWig = (function() { }; })(); +$(function() { + TPL.fetch('/vm'); +}); From 4f8ef6ee881aa1f1911378067627dab20a6e477c Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 11 Jul 2011 00:33:16 +0100 Subject: [PATCH 165/405] make dialogs wider --- priv/js/renderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/priv/js/renderer.js b/priv/js/renderer.js index 38f8538..d891515 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -10,7 +10,7 @@ var RENDERER = (function() { .dialog({ title: 'process_info(' + Pid + ')   ' + '(json)', - width: 460, + width: 550, buttons: { "Close": function() { $(this).dialog("close"); }, "Send Msg": function() { @@ -58,7 +58,7 @@ var RENDERER = (function() { .dialog({ title: m + ':module_info()   ' + '(json)', - width: 460, + width: 550, buttons: { "Close": function() { $(this).dialog("close"); }, "Reload Module": function() { From 5e3790abab804a9ffd4cb5e4c44c1b801a3b1590 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 11 Jul 2011 00:53:52 +0100 Subject: [PATCH 166/405] oh god, the sasl report formatting isn't finished :| --- priv/js/rb.js | 2 +- src/bigwig_http_rb_stream.erl | 8 ++++++-- src/bigwig_report_reader.erl | 16 ++++++++++------ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/priv/js/rb.js b/priv/js/rb.js index 47f75c5..e8cd1d0 100644 --- a/priv/js/rb.js +++ b/priv/js/rb.js @@ -45,7 +45,7 @@ RB = (function() { if(!data) { data = report; } - $('.data', el).html(RENDERER.stringify(data)); + $('.data', el).html(RENDERER.render_json_val(data)); }); $('.filter').click(function() { diff --git a/src/bigwig_http_rb_stream.erl b/src/bigwig_http_rb_stream.erl index f61d88c..53ccfc1 100644 --- a/src/bigwig_http_rb_stream.erl +++ b/src/bigwig_http_rb_stream.erl @@ -27,7 +27,7 @@ websocket_handle(Bin, Req, State) when is_binary(Bin) -> {reply, Bin, Req, State}; %% handle sasl reports sent form our custom handler -websocket_handle({bigwig, {bigwig_error_handler, Report}}, Req, State) -> +websocket_handle({bigwig, {bigwig_error_handler, Report}}, Req, State) -> {reply, report(bigwig_report_reader:fmt_report({erlang:localtime(), Report})), Req, State}; %% ignore other bigwig internal msgs @@ -45,4 +45,8 @@ websocket_terminate(_Reason, _Req, _State) -> ok. report(Report) -> - jsx:term_to_json([{report, [Report]}]). + Str = bigwig_report_reader:ascii_format_report("",Report), + J = [{report, [ {'_str', Str}|Report ]}], + Term = jsx:term_to_json(J), + io:format("~p\n", [Term]), + Term. diff --git a/src/bigwig_report_reader.erl b/src/bigwig_report_reader.erl index 685e9da..e7abf01 100644 --- a/src/bigwig_report_reader.erl +++ b/src/bigwig_report_reader.erl @@ -12,7 +12,7 @@ -behaviour(gen_server). -export([start_link/1, start/0, start/1, stop/0, rescan/0, rescan/1]). --export([load_number/1, load_list/0, load_list/1, make_filter/1, fmt_report/1]). +-export([load_number/1, load_list/0, load_list/1, make_filter/1, fmt_report/1, ascii_format_report/2]). %% gen_server callbacks -export([init/1, terminate/2, handle_call/3, @@ -455,14 +455,18 @@ find_report([], No) -> load_rep(Fd, FilePosition, _Device, _Abort, _Log) -> case read_rep_msg(Fd, FilePosition) of {Date, Msg} -> - Fn = "/tmp/rb_format.tmp", %% TODO this is horrid - {ok, IoDevice} = file:open(Fn, [write]), - rb_format_supp:print(Date, Msg, IoDevice), - file:close(IoDevice), - {ok, ReportBin} = file:read_file(Fn), + ReportBin = ascii_format_report(Date, Msg), {ok, Date, fmt_report(Msg), ReportBin}; _ -> {error, "rb: Cannot read from file"} end. + +ascii_format_report(Date, Report) -> + Fn = "/tmp/rb_format.tmp", %% TODO this is horrid + {ok, IoDevice} = file:open(Fn, [write]), + rb_format_supp:print(Date, Report, IoDevice), + file:close(IoDevice), + {ok, ReportBin} = file:read_file(Fn), + ReportBin. read_rep_msg(Fd, FilePosition) -> file:position(Fd, {bof, FilePosition}), From c930d4a3d4a7a6f20d79bc2a99e5df4ea18ffb03 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Mon, 11 Jul 2011 00:54:57 +0100 Subject: [PATCH 167/405] Add appmon README --- README | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README b/README index 59a5a59..755f8c8 100644 --- a/README +++ b/README @@ -96,11 +96,10 @@ through results. AppMon ------ -TODO write stuff here - - - - +A 'SpaceTree' browser of a node's application masters and supervision +hierarchies. Click nodes to browse the hierarchy. It will +automatically update unless live updates are disabled (via the button +at the top). URL schemes, blah From 4c8d3f543f9a71475a3b255dec477135d42a7ecb Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Mon, 11 Jul 2011 00:55:50 +0100 Subject: [PATCH 168/405] Rename README to README.md --- README => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README => README.md (100%) diff --git a/README b/README.md similarity index 100% rename from README rename to README.md From ffe031d88823e6f3e7e187d19585a1db2f1d9e01 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Mon, 11 Jul 2011 00:57:42 +0100 Subject: [PATCH 169/405] Additional README formatting --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 755f8c8..719541b 100644 --- a/README.md +++ b/README.md @@ -105,16 +105,16 @@ at the top). URL schemes, blah ================= -/vm lists running apps, releases, process count etc +* `/vm` lists running apps, releases, process count etc -/rb/reports lists available SASL reports -/rb/reports?type=error lists SASL reports of a specific type -/rb/reports/1 returns SASL report id 1 -/rb/stream websocket endpoint for streaming SASL reports as JSON +* `/rb/reports` lists available SASL reports +* `/rb/reports?type=error` lists SASL reports of a specific type +* `/rb/reports/1` returns SASL report id 1 +* `/rb/stream` websocket endpoint for streaming SASL reports as JSON -/pid/<1.2.3> process_info (POST to send msg to pid) -/pid/NAME as above, but using registered process name NAME -/pid/global/NAME as above, but using global registered process name NAME -/module/NAME NAME:module_info() stuff +* `/pid/<1.2.3>` process_info (POST to send msg to pid) +* `/pid/NAME` as above, but using registered process name NAME +* `/pid/global/NAME` as above, but using global registered process name NAME +* `/module/NAME` NAME:module_info() stuff From cad1d2fd8c88d50ca97545c7a658ade50c0ab9ef Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Mon, 11 Jul 2011 01:01:49 +0100 Subject: [PATCH 170/405] Crappy dashboard --- priv/js/bigwig.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index ecc2bf8..f6934f5 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -28,5 +28,8 @@ BigWig = (function() { })(); $(function() { - TPL.fetch('/vm'); + $.getJSON('/vm', function(json) { + $('#dashboard').append($('

    Dashboard dump

    ')); + $('#dashboard').append($('
    ' + JSON.stringify(json, null, 4) + '
    ')); + }); }); From 3bb78c8c6c3d21aaeb19d70fa109c93c9b3b5e00 Mon Sep 17 00:00:00 2001 From: Hunter Morris Date: Mon, 11 Jul 2011 01:03:05 +0100 Subject: [PATCH 171/405] Fix colour of 'dashboard dump' header --- priv/css/body.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/priv/css/body.css b/priv/css/body.css index fc3edbc..b8f036a 100644 --- a/priv/css/body.css +++ b/priv/css/body.css @@ -19,6 +19,9 @@ h1 { margin:0; padding:0.2em 0.4em; } +#dashboard h1 { + color: #000; +} #attrib { color: white; float: right; From 1d31483fbfbacc6f6db9447226d168fb791889c1 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 11 Jul 2011 15:41:07 +0100 Subject: [PATCH 172/405] use our project git uri instead of spawnfest one in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 719541b..bdd9d26 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Start up the dev server and head to http://localhost:40829/ or include as a rebar dep and start your own node: - {bigwig, ".*", {git, "git@github.com:spawnfest/beamspirit.git", {branch, "master"}}} + {bigwig, ".*", {git, "git://github.com/beamspirit/bigwig.git", {branch, "master"}}} rebar get-deps, compile, and then make sure your config includes the following so that binary sasl reports are generated (which are used by bigwig): From 0f4edae519efd3267f2ac86df377dd37a5b09ef1 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Tue, 1 Nov 2011 12:46:09 +0000 Subject: [PATCH 173/405] update for latest extend version of cowboy --- src/bigwig_http_rb_stream.erl | 23 ++++++++++++++--------- src/bigwig_http_stats_stream.erl | 19 +++++++++++-------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/bigwig_http_rb_stream.erl b/src/bigwig_http_rb_stream.erl index 53ccfc1..7eec51c 100644 --- a/src/bigwig_http_rb_stream.erl +++ b/src/bigwig_http_rb_stream.erl @@ -5,7 +5,8 @@ -behaviour(cowboy_http_handler). -behaviour(cowboy_http_websocket_handler). -export([init/3, handle/2, terminate/2]). --export([websocket_init/3, websocket_handle/3, websocket_terminate/3]). +-export([websocket_init/3, websocket_handle/3, websocket_terminate/3, + websocket_info/3]). init({tcp, http}, _Req, _Opts) -> {upgrade, protocol, cowboy_http_websocket}. @@ -23,24 +24,28 @@ websocket_init(_TransportName, Req, _Opts) -> bigwig_pubsubhub:register_client(self()), {ok, Req, undefined_state}. -websocket_handle(Bin, Req, State) when is_binary(Bin) -> - {reply, Bin, Req, State}; +websocket_info({text, Bin} = Msg, Req, State) when is_binary(Bin) -> + {reply, Msg, Req, State}; %% handle sasl reports sent form our custom handler -websocket_handle({bigwig, {bigwig_error_handler, Report}}, Req, State) -> - {reply, report(bigwig_report_reader:fmt_report({erlang:localtime(), Report})), Req, State}; +websocket_info({bigwig, {bigwig_error_handler, Report}}, Req, State) -> + Text = report(bigwig_report_reader:fmt_report({erlang:localtime(), Report})), + {reply, {text, Text}, Req, State}; %% ignore other bigwig internal msgs -websocket_handle({bigwig, _}, Req, State) -> +websocket_info({bigwig, _}, Req, State) -> {ok, Req, State}; -websocket_handle({websocket, Msg}, Req, State) -> - {reply, << "You said: ", Msg/binary >>, Req, State}; +websocket_info({websocket, Msg}, Req, State) -> + {reply, {text, << "You said: ", Msg/binary >>}, Req, State}; -websocket_handle(Msg, Req, State) -> +websocket_info(Msg, Req, State) -> io:format("Unknown msg to ~p ~p~n", [?MODULE, Msg]), {ok, Req, State}. +websocket_handle(_Msg, Req, State) -> + {ok, Req, State}. + websocket_terminate(_Reason, _Req, _State) -> ok. diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index 6a03d84..5e6fcdb 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -4,7 +4,8 @@ -behaviour(cowboy_http_handler). -behaviour(cowboy_http_websocket_handler). -export([init/3, handle/2, terminate/2]). --export([websocket_init/3, websocket_handle/3, websocket_terminate/3]). +-export([websocket_init/3, websocket_handle/3, websocket_terminate/3, + websocket_info/3]). init({tcp, http}, _Req, _Opts) -> @@ -22,20 +23,22 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too -websocket_handle({bigwig, {bigwig_stats_sender, Stats}}, Req, State) -> +websocket_info({bigwig, {bigwig_stats_sender, Stats}}, Req, State) -> Reply = jsx:term_to_json([{stats, Stats}]), - {reply, Reply, Req, State}; + {reply, {text, Reply}, Req, State}; -websocket_handle({bigwig, {bigwig_appmon, Stats}}, Req, State) -> +websocket_info({bigwig, {bigwig_appmon, Stats}}, Req, State) -> Reply = jsx:term_to_json([{appmon, Stats}]), - {reply, Reply, Req, State}; + {reply, {text, Reply}, Req, State}; -websocket_handle({bigwig, _}, Req, State) -> +websocket_info({bigwig, _}, Req, State) -> {ok, Req, State}; -websocket_handle(Msg, Req, State) -> - io:format("Unhandled msg to ~p ~p\n", [?MODULE, Msg]), +websocket_info(Info, Req, State) -> + io:format("Unhandled msg to ~p ~p\n", [?MODULE, Info]), {ok, Req, State}. websocket_terminate(_Reason, _Req, _State) -> ok. +websocket_handle(_Msg, Req, State) -> + {ok, Req, State}. From a368a42b95f303f192ec837416d99f1e4f3b1da3 Mon Sep 17 00:00:00 2001 From: Minsu Choi Date: Fri, 13 Apr 2012 08:57:47 +0900 Subject: [PATCH 174/405] fix bug using confval can't get proper value from application env --- src/bigwig_http.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bigwig_http.erl b/src/bigwig_http.erl index cf66833..f0b1bdc 100644 --- a/src/bigwig_http.erl +++ b/src/bigwig_http.erl @@ -39,7 +39,7 @@ dispatch_rules() -> confval(Key, Default) -> case application:get_env(Key) of undefined -> Default; - Val -> Val + {ok, Val} -> Val end. init([]) -> From 3e48734a9337a8d7f1373b06c30ab6ebb0a254a4 Mon Sep 17 00:00:00 2001 From: Steven Gravell Date: Mon, 30 Apr 2012 17:19:27 +0200 Subject: [PATCH 175/405] depend on cowboy and jsx --- src/bigwig.app.src | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bigwig.app.src b/src/bigwig.app.src index 12e484c..41db7ac 100644 --- a/src/bigwig.app.src +++ b/src/bigwig.app.src @@ -5,7 +5,9 @@ {registered, []}, {applications, [ kernel, - stdlib + stdlib, + cowboy, + jsx ]}, {mod, { bigwig_app, []}}, {env, []} From c7956518e3bf56b55a1812861916767d63b5ee20 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 5 Nov 2013 17:56:01 +0800 Subject: [PATCH 176/405] change the deps --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 757f86c..54fc33c 100644 --- a/rebar.config +++ b/rebar.config @@ -5,6 +5,6 @@ {deps_dir, ["deps"]}. {deps, [ - {cowboy, ".*", {git, "https://github.com/extend/cowboy.git", {branch, "master"}}}, + {cowboy, ".*", {git, "https://github.com/yuzhaoren/cowboy.git", {branch, "master"}}}, {jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} ]}. From df40c836329baba5275fcafb4608f5d7d330b114 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Wed, 6 Nov 2013 11:05:50 +0800 Subject: [PATCH 177/405] compile the code based on the latest deps --- rebar | Bin 102487 -> 152266 bytes rebar.config | 11 ++++++----- src/bigwig.app.src | 2 ++ src/bigwig.erl | 18 +++++++----------- src/bigwig_http.erl | 9 +++++++-- src/bigwig_http_appmon.erl | 4 ++-- src/bigwig_http_catchall.erl | 4 ++-- src/bigwig_http_etop2.erl | 4 ++-- src/bigwig_http_module.erl | 4 ++-- src/bigwig_http_pid.erl | 4 ++-- src/bigwig_http_rb.erl | 4 ++-- src/bigwig_http_rb_stream.erl | 6 +++--- src/bigwig_http_static.erl | 4 ++-- src/bigwig_http_stats_stream.erl | 8 +++++--- src/bigwig_http_vm.erl | 5 +++-- src/bigwig_pubsubhub.erl | 8 +++++++- src/myserver.erl | 8 +++++++- start-dev.sh | 2 +- 18 files changed, 62 insertions(+), 43 deletions(-) diff --git a/rebar b/rebar index 4b5090b3471990722bcbac188e4c7e170430508d..b80b1edbee36f25ed741f613bf9c67b8b274f9e9 100755 GIT binary patch literal 152266 zcmZ^~V~{97vnANJZQHhO+qP}n?%TF?+qP}nwmtW~w>ukO>`X;PA>u?v{m4R|JVi`M z@9OMCZ)9mlZ))d8VCrn_Wa;1nMN0aw5)#ll7!o*{8W}p#|4(cB4+o_n4Ge+;@Lwe_ zC|dBp2men4_OJgR;Qt*1{l78)WsLs+1w#C{F}?J|_ZlPsfO0$l0JQ%Enwz@VJGjsp znHt*qYI-_guX^qt1MO-5k=HUHR7@{PMMG)^qNIaV0HT=pBY_y0gM?1;i3SKHg+u}| zFsPvBqfm@G5=O+;Utg;~o{+N{tv?b@!#z#NX=2V7Z_1%5-+r5ti1#`Q(F!*y^_G#3;>HWMwGU#y24AZ#HT6td5a6@BG zr8-@r3YBqSF{m-4x=j6kP%cd`YcX7@j9-=V!rd%YqT-15pxV8mG5=zjKd+GKa5ITg z%EGpXGmcW3Im*JKLV+qC-e=dNVv{`w7k3oR=~ww@F<{Q=@j-NM{M07!c(okHv~u~2 zLITc|s%WRGL{_a?XY5j4lD2(CLXkUvkuY2@W1(xs!?jvv-WKpwqDN^NSH+mN=g+kJ750cYK^8!T2w1UjJ$v{{hZv}RwH z>t(-E&a*4c#F{--d6hvl+6fzru^&iRB`TOQ=5vTkglgwFl*Z0F)UwT?;7!S$g+a?V z$u7uU6FZ`67`d;ZYcF8eqmBJ^#YUQh)>Y@Cias#%vI=iZU1}Jsj{6_Vkq4Bfb{I4# zWCepBh7%WjG}45hzuBC_UTRwsD~z%>Mnt4JV-0|$sBqnT0>`NpJsUc$nF*neYDTUnPrtS#^92kF(t!8z++5`2oUAS_z(>c*4>2= z0>~?bv@jBe+K6XhC3L=~+s;r$+|o>nj$~F;M}WLD7noDv{+?zom=b+RDv%Tc#~{#& zG<=h=S`-^5?TqC)ZRa^F;g3f6W)pz(q4UT1(7F55k&ov~n8dxA#_uv)q~KERn5Eph zLk;aiBIU_&BtD5tMmUqjm#jwz&bChm4Gn=A4k42KeDuc)E6fj z8@|+DE|3y;hZ^@Xm|=JQYMCX@snB`jgiI#Vh%50SftCr0+>jsSJ8lr}VH2+nx(XNV znDl2+-HS)kiwX%|NcYM(WjkZB2UEh2j~4?YT`|nCR*plU3C@p_)-Xso;g^8{uVK4* z1FeyBgGfe28UVorqB3L_#RcNlar%V9GwDRdmDa-y8d2-@>6#_Za)8I>g9>F9=yh~E z;=4Xd=&;Zc5yB=x$HrrK**Rv4PRlMXicBGT&Vf)Un^)~M#}|K*U=j8E5nYauB0m!e zbpI(#18AlKzcZR&jY^-eTjU1^n8_g+dINGwCsH#axbWj<3=xgix<`U?sCSG6(s2UK z?*&7E3_x(8t%-fV7~xr2V*>UB`~eCSWnWkxjHE-YGnjV*j!da*un@(lL-8IqU?IqTAF4n!m~gMwHL`1vZ`PW89}GRMkQlDIHDG(-AkEw! z^L=C16(;(_pm%xu?0oPCzmBZXW|i7R`w7zm(ZW&)Nulh(D?7U6q;gQOYVhzbgy%a~ zP(Chd;AH<*4)|sH@JFrMLA-i8&}l_r+yg8UekH?CNEXY25u@i?s@5u+V!RNoPb#@- zML^o_zEJ|v3^NlV2Q4`R-Hwi4x~8j|d5$S!nTX;qz_?nZx8n+H?54(&RX+APz*tkL zWBCRP_qKMH6XMlz!;TJ}?!)|sQ--!L z13hf4UbC9PCQbB8RubV>tukOjh@%Eq{_&Vn1ruDb3ip(xOCks-a81A%4wONvGrRIi zKFt2|xMIZCJhIh>0Md3rt%#k2-KU@xfIG)lbj5uU3u`c*g9$0D3;d0V;^;DPmBF4J znrr4Ii3Bfz#SN*ZRnrdeU0ZbLRxtZt3Y38wRrLpq=neuQ7D?>d#-&6UO~Bz;um|D0 zbRo3?#4bcC4`P)&8_bJVsAq0x*EP%gOw^j>jG`J3WfUuS)+IZY5EwwqRC{>ZT|BKG zM2cNmny8R_+2)7qu%wbRLbU@$j+wbkevNLvMlWCWO7~dR?nZ^G?Ith#T2=aE8>qc$ zHG^rLw-cv%Cs)+fW249B0c*>?I_fRppLvTN6N1pRuW4se|EJ6(eRI%s3<`jLU9$Re z_L$FhJvlfr2bG(!w5#M$i#(bVAP%gOW-Qrx^Sm*rN-0=hjP(90KH%n^TN2ZGVdz=V zaFXakXBB@4twm;UKQ>+Lh+E?vJ4C+BM!+kndP#6jOB2rdF=RGNVW!jJ! zahckXR~P*Wfh+8li-0)c$Sjs>ur~FK^Zn0z+8QX~y5XX&b7KM;dru*@+=d012J>kM zae``;nKfWm;4DZsEt^Ch)YWq}It`Q+14P&1pjf2YySb@qz*ChApd|++v^gfsfgS69 zn71bLZWI}~PYZ|#JOnF@>NG}O_)(w%f`TN5Be+cjb~UIV$5+MSad`t>ux>PKGb))q z+Sg6b`sQU9Ax(j2bikm1Us3?@h?}5$?vCT0Awlp;cgBP~9-zK+_W269G`+CYN2CBo z4m?Xy?yClhGj`Ykzgz$v001E_@qugr0Bxj&|CS05um(Jze5N`84lorBwILCYfVwM5 z>_#pkGt%1m{RW4&&6myr4=Qx)dltu;+vyqSX>O~pq1#Fy0thtaogPR8UhMrtANwwjP`zE{^oY#Z*HI4Qa6X*rt2dx7XM$7??(Le^Km8FSlenxIX!5%oJ zg|JHG0b&xB2BXqAQizE9v8Pf0-lW}Mw72!g8+WD?*aIg!+hCp|r6bU8hp@lbR-^h^1_LWGCXn3|V!opXKhc#S1WG+qmN7TbJU17Bbbew=>szK?p&%WzE>kx)JjelxTam??)8 zQD)hg)ZiL~1M&UAYZ0JPPt3pWO>8Z&BXNV`+iQ&0$={`cErThB5fk^GcR(R-OG@p@ zrAou_iBL(`0z&LH#_75?4bRSTr{6V0QgBCE;XNuO~HkJQ%q0K5q{jJXWU^@fVi^=U%(t+ z#~f(=!bEpj=Ln7mM&4th@=~)@P=kcoB{A8?w-E}N)pwevelG87Q4g_#7{wVd9zji! zeuHk-!8U?bjsUr5gP!xF_f9pQc!9UYY6ijHv!n9h*${EJ?#7*5nI= zi(qn^`h`5vuRc%DVx9*2auW9XdOHQLVima{HFP?^g{-JL^;=skvx7G=aq4v_N?BuC` z__kO>&Ru9w#qsDyS{?tL@X5wqB!hy#!f3v|sJ{fzOl6bxHaSzxLHq=;X!lOkexTJ0 z&WxaTaKXWR6jvDN;(7`jGOVN?92Zv+eMq#=>RR_zn$o<`7n;hrkp42iTEulh9f3}z zb{i_FvZ~S{qWmH0ofxN$_Mr5Fp515z^iIvLbr}`&d!))H8c)Q)u>vulgm-o9j6g#> z0!1vlcZN`L8$srsrG$e=B88KDLf|XBI@s+8u?2S(|uVJX{z{j#j zLqc%*?8?16X7kuU8F3|81Xs2V!=B6myM>$}e7k8i*dEet77bSS1%!v%`-ulMlxW?u zI8WBu$VgZEhj+P7{grYNW1M%-J`j1$^p3X}7C<=I0UWw(5xdCR1+0CUFKf{=0-#zW zV3}RBl=+}IB{%L;rhWw!Eqm^oXXv4^NP`eTDTvyCruNYc>rQM8wL;L$#vm^#wg&<> zMKy?SlSj)vibBw!&_n<5$N3n=AYc1PuL#FP^37BMHMxTx)kyM^X~71aFjFu3jm;JL zD7Lul1jEAe`Pt+nd87j9@3Jdo2-%m;-ImN5j-Y)ZqY0?yON+oSzeS?PkQ1-*Y+%`XO6_Wp zHx|m3Hp`XP#Odjax32ChUcQyb!L97CTRhqaxiwA~JtlE=w92t-iH~0Tbd}7Uq;X}~ z{o;F|zB`+5+hY8gEvx)(Zqw2KGt*MA0#+Kebi%Z`;+FMWU>on4 zuRq*!>`L{>62Sq_)A?D1_ZlwZp!cy{NI&ayd%S+HT;`8LZ5Fe?Z10)1)A2N^N-od# ztLN#Gcv+zgzDv)`($f69+KcN!@mJK==4V(PKV{qh_#^r)x0ly*{xj!opLd+Ub8E-h zaF?F|U(;!KJ+C+SC+ByOwr=}rI)~rQ_kBj6+;7@%fcJ65A2j!uX4&y5I9%_>`?DAS zed>2PY|ek{(><@NV?T1io)>$SX;Vs5OS>_^@p#ooV$E-UHwWxWLni>O(R4=evei^C ze8v1xzrk{X-*_}`Wf*#nZT)Msz;*M9(gHEd4@GOOG+ubqV3P-?X0jAs5PF^IGv9T? z1$0A8TP%3R=rO#YrvBYsSle{5b)7Fu8a7i!oqp0b1CYBnrS!QirG5owxGkdoqI0^G zk{`gfK-ZY53AAMzl}+}3&XsJVV|wmnvm=NXJ{I+2+4W*}CIjd=j`aw`PQO`#+eXm< z&rvq_xAnV|n~a~7zN^7)eBY63Z$jqWxFm`)*?8!_LdLO6q@hHzMqx~4HJz&H@KA;{#CwMwfBdk(6IXPoOaM8|EYWA2KIYtl~!X zlete17CMr z%oCoQw=|-Y@H{fhB%6WFL~%Gt!;D*zR!XI58wA;OTN`BL-JG8~+9c)Ou7tJP;N>lf zJ)^Qwm1@)ZOnvE9ql6JY1h4Mek=q&=kI;$zhuYtTzkDIU`(2N3g!~PqH*0sY>|L>E zK>c0#OYsMVf0BO~`wRKY@(1RZ=$FesjeoRvuy?$7x_9)K^EdrV{HyHC{LB7L`iuWT z|L%9-m;9IX_x9`d{Z|o3s^~ybL{#ldD!M`LT#1cpTdHoM%tl>lB0ouu#Z;(*>XkA) z1$ct*81PuoRE)DWTPas9S6xqeM`4BvJ;gosH6@R#SA|cBPtmXFtJ_KPo&4B+f}dKS zRO8qmS!W>8LZS_sqn@aqs-CN!u7HdSsb9pMD6x&To;p$LO6~|r%ZP-r_6wi+_Tr4; z7yds9E*AW}7-4t-fE0890F3`6xNKdWT?~yaO#hqW3e|viL0-+k^EA2C+Yd|>A6XkQ zyThi}bygE^2PkE9FMAi~g+PDsb zHI#Q-Bw?YEZpkXPL2i)Dx*{}Xm+i=+4ROQwHT~tr+6;QhX5DK4?f!NDedX^^y4_Pi z9z@|ixH;cgNR66hkdfmbWYA+tmMaY^pd3bunk6Ap1f{fMNq_-0_8d>aij+MWDr4N1 z0$DC@knjgf@kX{%0;l>6n*aLaByYfj1P#n(S=yzuCQE&yz*Buhws27#RCI5^!2o#h zXm+sgqQa<*RTWjARYZlRDk&072oS(a30h`sXi9{nJm6UgRBQz&E<}l@q6q|;fCzC! zrtGLX9xIkqQEW0%|F z^$)@k5v%DAcKncrfG~yezK<0FYYLW4mc*#bqh;ZNl@w%_vX$0&4un{86sEv9X`+Ty zhRPD>>I0Dr-fUJx6+$=<=tEvU=6abx2jQF`YbtUi{lpO+LZs|ax&n9O3f>s&pu;Z+ z>o9W$KkNt{Mktxyp)(YfNAlXn0Nsh_fhmYAOY|0#Y|C9tRk-b~p(*<$gk4&?m3Igs zQ9V}uU6*x;ExzHf=Sl>_5*v}LBwW1nkT8zfw7H?k0MyVF-U+7d6ww4-sI7WKG39_W zK}`Unj1ynE*|gTclygmnaQgw_jtGkU2ZD_@$GVr*JX`Yys)(%o`D{s;245k4aK^D6F_bc zy(9OUH@eO0nh}0Br#4V|@m4K(W*VW7pkh*v)azalvud#UOE!g)wLE#2mWQ9RFL03w;&chJ7NK8iK z8t=)5tjFX83nACF+JV^e$_N(~3NrLdfJu#LO`J_@5&A%DvI%HFP-&YPz#}vXszK^Y zFcOlc=|6yd*7U?sC&*ORCTKz1WQt+B)@bmUTxJH-PaYfS-nNQ{LsBx5#_o%4%=|Ygu*xsvkap!j|5?2 zQLP%10#J{Kr9ueP)h_M81-D(pT(&$4(W*~qU3bafkiu!f0Hp+r-~d&az)u?NKv6_8 z9mqlmXbnLxf!+fC70K3~8ZaV7q7Qf z8m>XuFhpqWlduzXS#qK#sq(!C6bfNjo+)Kfu|%wEZ*#g6ntBmJsaC=!zd$`b5CQ*- zTe#T|(~0W?fssom|3JWy)F%;jL*hJ?b0iOVXt3Jvj2`F+hjA+M%6UTj%K6igj7*P| zB@jlMWF$EzL&D9eBq_%^uDyNKp)XuT^8gE5)5D)G-nC70Rvh60IP>>q&?hLQe{>lQ zah*jaRZuZ4SVgLjC{7AnMKqPJSj(K7`RWFS+s1jLBM=l|zzw-~hpkXvm=)n&wF4)R zgLDCQ%}%={Y~_z>ZID_~vQXp^4^1>y<@C`x&6+xN#XkHUuSZI%*I$Z$mPgwlAbUj5!}L8N~-X=#+79f8iD-$do^5!0@Mr`b+|iq8HnogO%O}{iUKbQ7u2J! zfniA{ZkU5KjBxD(-sbG1zZ;LHd5T+m0RlV-1z_8N|D>Uy2EQv%VY<4<}m*% z&E=Ec95l`gN$+$;!LtIMwZ-fkC^pM6m_?A+z=tF|Eu1!~Eny*h3_2}vbI6IE4W0XA z^R@Z;e(k%$V!Gq1klF|V^-X)V`d=WG-~`H_Wdt^@FFh5afZcH9fTu4(6IC+PYp;n) z)+Jf(LNQ_9gbvn!m97>{Nu4+%W9dGjL#yKLan{i3PegMXC}A_!MK^ysfnLxV2Z;vk z7`sfloUk5(GI);a?#Q?e`sLk{vuhk2xVpN?om4I0?kH=fLU3}`V?a%biW;#l&50@c zDoo85S!*sfmIDKL|PAgqh){_m%)E zLgw{?kBDYTf~Gp~{4Xe79q1=fX*c~Q2;mx?Xc2nJr%XnO)3gCPZV)om2!wGo55Th_ zE!cN~Wi5qeE_dp7XgkuGnD34v;jkj2KMXv#SaT90q7Hv$h5Zud!ASS5jyE*D4)ev( z%;Vw%5+`+Z$T8M4Y)#00mIkqvg4oufu%aS$gR(%jm{)@HU${eTCLzuN2yxeixCA0J z$F}6p+Tj8g;IHK7q)%P3HsoxBxwLmDYz<;BVSQ z){|?Por`9U+xZft45qb2KjzJnpZ>;ZnWzvsciVtZKBFO8ldqcUj<(B|M{8+G(kp-X zny3erG3BFeJvB)-tUo_?GM{!ob*b~;20m_+9ThM*pWNkw7~|ZKaoPmJR9pc@TZX>` zLAtyIQE=HWDF6ul#ba7lpS1;oH3sIPVl9w`-7-t_V`$)P;!p=7=O8FNCTr3>`7mUK zYXd^Q0N>YUK>;GPLtqkc3gJn7%$Um$8*o7uI<-C&%a0@#$36{Ttv7GWee~wy$?mm) zad&dxf8IjSn}*Gr)0Lay&xUXHr>``4=57Hpq|53B`4kwO0}`7sAoGJ~?h2@wG9dGZ zKMbFAjwk8!4xh&0IXD6<$n9&Ubg0=1X|bXJ!gB%k(8g{><7Vz8a8JoBW)^{9^a;e4tMK~> zt+giuY&_>kKxLqqgc$@Oh7TLgM})EQ0WWSs6@!cM!N?9Qcrpqu-9@(r(*+P_I6~{! zFdHjyi7aSD8F`@biDR5FtR5>IrjZG)mp@j+a-uL>#{G0QuWG!qVK@`RXb@x{binW+ zk4dAEsj8Q^bu%5eVP{CW8EY^Mu9r7(!SuK&I)%$L@@imss%DoaD{=O^Dd9AN8`J

    0H5fiQ6AkGVYzrcaF)XWu2iLp8#eu>&RQgmz>Xh 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�v!C#7v>?rOWK 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

    Is}_21!;&U2gEZc5-mQ*{Q$>QCy7c1|u9^ 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 diff --git a/rebar.config b/rebar.config index 757f86c..1dc3a1d 100644 --- a/rebar.config +++ b/rebar.config @@ -1,10 +1,11 @@ {erl_opts, [debug_info]}. %, fail_on_warning]}. - -{require_otp_vsn, "R14"}. - +%{require_otp_vsn, "R14"}. {deps_dir, ["deps"]}. {deps, [ - {cowboy, ".*", {git, "https://github.com/extend/cowboy.git", {branch, "master"}}}, - {jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} + %{cowboy, ".*", {git, "https://github.com/extend/cowboy.git", {branch, "master"}}}, + %{jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} + + {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, + {jsx, ".*", {git, "git://github.com/spawnfest/jsx.git", "master"}} ]}. diff --git a/src/bigwig.app.src b/src/bigwig.app.src index 41db7ac..1d701bf 100644 --- a/src/bigwig.app.src +++ b/src/bigwig.app.src @@ -6,6 +6,8 @@ {applications, [ kernel, stdlib, + crypto, + ranch, cowboy, jsx ]}, diff --git a/src/bigwig.erl b/src/bigwig.erl index 7280c79..d92b01e 100644 --- a/src/bigwig.erl +++ b/src/bigwig.erl @@ -1,18 +1,14 @@ -module(bigwig). -export([start/0, stop/0]). -ensure_started(App) -> - case application:start(App) of - ok -> - ok; - {error, {already_started, App}} -> - ok - end. - start() -> - ensure_started(crypto), - ensure_started(sasl), - ensure_started(cowboy), + application:ensure_started(ranch), + application:ensure_started(jsx), + + application:ensure_started(crypto), + application:ensure_started(sasl), + application:ensure_started(cowlib), + application:ensure_started(cowboy), application:start(bigwig). stop() -> diff --git a/src/bigwig_http.erl b/src/bigwig_http.erl index f0b1bdc..b9de8db 100644 --- a/src/bigwig_http.erl +++ b/src/bigwig_http.erl @@ -4,7 +4,12 @@ -export([start_link/0]). --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). -record(state, {}). @@ -51,7 +56,7 @@ init([]) -> error_logger:info_msg("Bigwig listening on http://~s:~B/~n", [IpStr,Port]), %% %% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts - cowboy:start_listener(http, NumAcceptors, + ranch:start_listener(http, NumAcceptors, cowboy_tcp_transport, [{port, Port}], cowboy_http_protocol, [{dispatch, dispatch_rules()}] ), diff --git a/src/bigwig_http_appmon.erl b/src/bigwig_http_appmon.erl index aca9cc6..d907c85 100644 --- a/src/bigwig_http_appmon.erl +++ b/src/bigwig_http_appmon.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_appmon). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). -define(P2B(P), list_to_binary(pid_to_list(P))). -define(L2B(L), list_to_binary(L)). @@ -46,5 +46,5 @@ not_found(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. diff --git a/src/bigwig_http_catchall.erl b/src/bigwig_http_catchall.erl index ea7c828..c1d21a2 100644 --- a/src/bigwig_http_catchall.erl +++ b/src/bigwig_http_catchall.erl @@ -1,6 +1,6 @@ -module(bigwig_http_catchall). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. @@ -10,6 +10,6 @@ handle(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], Body, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. diff --git a/src/bigwig_http_etop2.erl b/src/bigwig_http_etop2.erl index ba41294..4ea98e8 100644 --- a/src/bigwig_http_etop2.erl +++ b/src/bigwig_http_etop2.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_etop2). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. @@ -26,7 +26,7 @@ not_found(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. handle_get(Req, State) -> diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index 0cad5a5..a0ddb89 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_module). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. @@ -35,7 +35,7 @@ not_found(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. %% Reads and modifies module_info a bit to be more json friendly diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index 2440611..3b451c8 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_pid). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. @@ -30,7 +30,7 @@ not_found(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. handle_get_pid(Get, Pid0, Req, State) -> diff --git a/src/bigwig_http_rb.erl b/src/bigwig_http_rb.erl index ce14565..818d0a0 100644 --- a/src/bigwig_http_rb.erl +++ b/src/bigwig_http_rb.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_rb). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> bigwig_report_reader:start(), %% will only be started once anyway, registered name @@ -35,7 +35,7 @@ handle_path(Path, Req, State) -> {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. diff --git a/src/bigwig_http_rb_stream.erl b/src/bigwig_http_rb_stream.erl index 7eec51c..96144e2 100644 --- a/src/bigwig_http_rb_stream.erl +++ b/src/bigwig_http_rb_stream.erl @@ -3,8 +3,8 @@ %% -module(bigwig_http_rb_stream). -behaviour(cowboy_http_handler). --behaviour(cowboy_http_websocket_handler). --export([init/3, handle/2, terminate/2]). +-behaviour(cowboy_websocket_handler). +-export([init/3, handle/2, terminate/3]). -export([websocket_init/3, websocket_handle/3, websocket_terminate/3, websocket_info/3]). @@ -15,7 +15,7 @@ handle(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], <<"Websockets only here pls">>, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. websocket_init(_TransportName, Req, _Opts) -> diff --git a/src/bigwig_http_static.erl b/src/bigwig_http_static.erl index 4258395..9f6f122 100644 --- a/src/bigwig_http_static.erl +++ b/src/bigwig_http_static.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_static). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). -export([html/1, css/1, js/1]). @@ -33,7 +33,7 @@ send(Req, PathBins, State) -> {ok, Req2, State} end. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. html(Name) -> diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index 5e6fcdb..e6606ab 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -2,8 +2,10 @@ %% -module(bigwig_http_stats_stream). -behaviour(cowboy_http_handler). --behaviour(cowboy_http_websocket_handler). --export([init/3, handle/2, terminate/2]). +%-behaviour(cowboy_http_websocket_handler). +-behaviour(cowboy_websocket_handler). + +-export([init/3, handle/2, terminate/3]). -export([websocket_init/3, websocket_handle/3, websocket_terminate/3, websocket_info/3]). @@ -14,7 +16,7 @@ init({tcp, http}, _Req, _Opts) -> handle(_Req, _State) -> exit(websockets_only). -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> exit(websockets_only). websocket_init(_TransportName, Req, _Opts) -> diff --git a/src/bigwig_http_vm.erl b/src/bigwig_http_vm.erl index 704deeb..9ab2310 100644 --- a/src/bigwig_http_vm.erl +++ b/src/bigwig_http_vm.erl @@ -2,8 +2,9 @@ %% show details on the VM, releases, apps, etc. %% -module(bigwig_http_vm). + -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). -compile(export_all). @@ -16,7 +17,7 @@ handle(Req, State) -> {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. diff --git a/src/bigwig_pubsubhub.erl b/src/bigwig_pubsubhub.erl index 62aa19d..25dc8cf 100644 --- a/src/bigwig_pubsubhub.erl +++ b/src/bigwig_pubsubhub.erl @@ -1,4 +1,5 @@ -module(bigwig_pubsubhub). + -behaviour(gen_server). -define(SERVER, ?MODULE). @@ -12,7 +13,12 @@ %% gen_server Function Exports %% ------------------------------------------------------------------ --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). -record(state, {listeners=[]}). diff --git a/src/myserver.erl b/src/myserver.erl index 7fdc187..6b58145 100644 --- a/src/myserver.erl +++ b/src/myserver.erl @@ -1,4 +1,5 @@ -module(myserver). + -behaviour(gen_server). -define(SERVER, ?MODULE). @@ -12,7 +13,12 @@ %% gen_server Function Exports %% ------------------------------------------------------------------ --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). %% ------------------------------------------------------------------ %% API Function Definitions diff --git a/start-dev.sh b/start-dev.sh index e668997..f93109b 100755 --- a/start-dev.sh +++ b/start-dev.sh @@ -2,4 +2,4 @@ cd `dirname $0` make mkdir -p log/sasl -exec erl -sname bigwig -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -config sys.config -s reloader -s bigwig +erl -sname bigwig -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -config sys.config -s reloader -s bigwig From 2c0f95104d73135e3f1121e4e066cab56c6396f6 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Wed, 6 Nov 2013 11:05:50 +0800 Subject: [PATCH 178/405] compile the code based on the latest deps --- rebar | Bin 102487 -> 152266 bytes src/bigwig.app.src | 2 ++ src/bigwig.erl | 18 +++++++----------- src/bigwig_http.erl | 9 +++++++-- src/bigwig_http_appmon.erl | 4 ++-- src/bigwig_http_catchall.erl | 4 ++-- src/bigwig_http_etop2.erl | 4 ++-- src/bigwig_http_module.erl | 4 ++-- src/bigwig_http_pid.erl | 4 ++-- src/bigwig_http_rb.erl | 4 ++-- src/bigwig_http_rb_stream.erl | 6 +++--- src/bigwig_http_static.erl | 4 ++-- src/bigwig_http_stats_stream.erl | 8 +++++--- src/bigwig_http_vm.erl | 5 +++-- src/bigwig_pubsubhub.erl | 8 +++++++- src/myserver.erl | 8 +++++++- start-dev.sh | 2 +- 17 files changed, 56 insertions(+), 38 deletions(-) diff --git a/rebar b/rebar index 4b5090b3471990722bcbac188e4c7e170430508d..b80b1edbee36f25ed741f613bf9c67b8b274f9e9 100755 GIT binary patch literal 152266 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

    0H5fiQ6AkGVYzrcaF)XWu2iLp8#eu>&RQgmz>Xh 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�v!C#7v>?rOWK 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

    Is}_21!;&U2gEZc5-mQ*{Q$>QCy7c1|u9^ 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

    404

    ">>, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. diff --git a/src/bigwig_http_catchall.erl b/src/bigwig_http_catchall.erl index ea7c828..c1d21a2 100644 --- a/src/bigwig_http_catchall.erl +++ b/src/bigwig_http_catchall.erl @@ -1,6 +1,6 @@ -module(bigwig_http_catchall). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. @@ -10,6 +10,6 @@ handle(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], Body, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. diff --git a/src/bigwig_http_etop2.erl b/src/bigwig_http_etop2.erl index ba41294..4ea98e8 100644 --- a/src/bigwig_http_etop2.erl +++ b/src/bigwig_http_etop2.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_etop2). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. @@ -26,7 +26,7 @@ not_found(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. handle_get(Req, State) -> diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index 0cad5a5..a0ddb89 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_module). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. @@ -35,7 +35,7 @@ not_found(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. %% Reads and modifies module_info a bit to be more json friendly diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index 2440611..3b451c8 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_pid). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. @@ -30,7 +30,7 @@ not_found(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. handle_get_pid(Get, Pid0, Req, State) -> diff --git a/src/bigwig_http_rb.erl b/src/bigwig_http_rb.erl index ce14565..818d0a0 100644 --- a/src/bigwig_http_rb.erl +++ b/src/bigwig_http_rb.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_rb). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> bigwig_report_reader:start(), %% will only be started once anyway, registered name @@ -35,7 +35,7 @@ handle_path(Path, Req, State) -> {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. diff --git a/src/bigwig_http_rb_stream.erl b/src/bigwig_http_rb_stream.erl index 7eec51c..96144e2 100644 --- a/src/bigwig_http_rb_stream.erl +++ b/src/bigwig_http_rb_stream.erl @@ -3,8 +3,8 @@ %% -module(bigwig_http_rb_stream). -behaviour(cowboy_http_handler). --behaviour(cowboy_http_websocket_handler). --export([init/3, handle/2, terminate/2]). +-behaviour(cowboy_websocket_handler). +-export([init/3, handle/2, terminate/3]). -export([websocket_init/3, websocket_handle/3, websocket_terminate/3, websocket_info/3]). @@ -15,7 +15,7 @@ handle(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], <<"Websockets only here pls">>, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. websocket_init(_TransportName, Req, _Opts) -> diff --git a/src/bigwig_http_static.erl b/src/bigwig_http_static.erl index 4258395..9f6f122 100644 --- a/src/bigwig_http_static.erl +++ b/src/bigwig_http_static.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_static). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). -export([html/1, css/1, js/1]). @@ -33,7 +33,7 @@ send(Req, PathBins, State) -> {ok, Req2, State} end. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. html(Name) -> diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index 5e6fcdb..e6606ab 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -2,8 +2,10 @@ %% -module(bigwig_http_stats_stream). -behaviour(cowboy_http_handler). --behaviour(cowboy_http_websocket_handler). --export([init/3, handle/2, terminate/2]). +%-behaviour(cowboy_http_websocket_handler). +-behaviour(cowboy_websocket_handler). + +-export([init/3, handle/2, terminate/3]). -export([websocket_init/3, websocket_handle/3, websocket_terminate/3, websocket_info/3]). @@ -14,7 +16,7 @@ init({tcp, http}, _Req, _Opts) -> handle(_Req, _State) -> exit(websockets_only). -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> exit(websockets_only). websocket_init(_TransportName, Req, _Opts) -> diff --git a/src/bigwig_http_vm.erl b/src/bigwig_http_vm.erl index 704deeb..9ab2310 100644 --- a/src/bigwig_http_vm.erl +++ b/src/bigwig_http_vm.erl @@ -2,8 +2,9 @@ %% show details on the VM, releases, apps, etc. %% -module(bigwig_http_vm). + -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). -compile(export_all). @@ -16,7 +17,7 @@ handle(Req, State) -> {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), {ok, Req2, State}. -terminate(_Req, _State) -> +terminate(_Reason, _Req, _State) -> ok. diff --git a/src/bigwig_pubsubhub.erl b/src/bigwig_pubsubhub.erl index 62aa19d..25dc8cf 100644 --- a/src/bigwig_pubsubhub.erl +++ b/src/bigwig_pubsubhub.erl @@ -1,4 +1,5 @@ -module(bigwig_pubsubhub). + -behaviour(gen_server). -define(SERVER, ?MODULE). @@ -12,7 +13,12 @@ %% gen_server Function Exports %% ------------------------------------------------------------------ --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). -record(state, {listeners=[]}). diff --git a/src/myserver.erl b/src/myserver.erl index 7fdc187..6b58145 100644 --- a/src/myserver.erl +++ b/src/myserver.erl @@ -1,4 +1,5 @@ -module(myserver). + -behaviour(gen_server). -define(SERVER, ?MODULE). @@ -12,7 +13,12 @@ %% gen_server Function Exports %% ------------------------------------------------------------------ --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). %% ------------------------------------------------------------------ %% API Function Definitions diff --git a/start-dev.sh b/start-dev.sh index e668997..f93109b 100755 --- a/start-dev.sh +++ b/start-dev.sh @@ -2,4 +2,4 @@ cd `dirname $0` make mkdir -p log/sasl -exec erl -sname bigwig -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -config sys.config -s reloader -s bigwig +erl -sname bigwig -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -config sys.config -s reloader -s bigwig From b352970664ca81d6e41fd3edc544bee30c64bdcf Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Wed, 6 Nov 2013 11:23:56 +0800 Subject: [PATCH 179/405] add fail_on_warning compile opt --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index cd287a7..f468963 100644 --- a/rebar.config +++ b/rebar.config @@ -1,4 +1,4 @@ -{erl_opts, [debug_info], fail_on_warning]}. +{erl_opts, [debug_info, fail_on_warning]}. %{require_otp_vsn, "R14"}. {deps_dir, ["deps"]}. From 35d4c5d0f17f80b422ad7d66facb26566ef94234 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 01:01:13 +0800 Subject: [PATCH 180/405] cowboy_static replaces with bigwig_http_static --- src/bigwig_http_static.erl | 57 -------------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 src/bigwig_http_static.erl diff --git a/src/bigwig_http_static.erl b/src/bigwig_http_static.erl deleted file mode 100644 index 9f6f122..0000000 --- a/src/bigwig_http_static.erl +++ /dev/null @@ -1,57 +0,0 @@ -%% -%% show details on the VM, releases, apps, etc. -%% --module(bigwig_http_static). --behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/3]). - --export([html/1, css/1, js/1]). - --compile(export_all). - -init({tcp, http}, Req, []) -> - {ok, Req, undefined_state}; -init({tcp, http}, Req, OnlyFile) -> - {ok, Req, OnlyFile}. - -handle(Req, undefined_state = State) -> - {[_|Path], Req2} = cowboy_http_req:path(Req), % strip <<"static">> - send(Req2, Path, State); - -handle(Req, OnlyFile = State) -> - send(Req, OnlyFile, State). - -send(Req, PathBins, State) -> - Path = [ binary_to_list(P) || P <- PathBins ], - case file(filename:join(Path)) of - {ok, Body} -> - Headers = [{<<"Content-Type">>, <<"text/html">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), - {ok, Req2, State}; - _ -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"404'd">>, Req), - {ok, Req2, State} - end. - -terminate(_Reason, _Req, _State) -> - ok. - -html(Name) -> - type("html", Name). -css(Name) -> - type("css", Name). -js(Name) -> - type("js", Name). - -type(Type, Name) -> - file(filename:join([Type, Name ++ Type])). - -file(Path) -> - Priv = priv(), - file:read_file(filename:join(Priv, Path)). - -priv() -> - case code:priv_dir(bigwig) of - {error,_} -> "priv"; - Priv -> Priv - end. From 5dae4da48247e854761af58b4811a9cf81151d39 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 01:01:13 +0800 Subject: [PATCH 181/405] cowboy_static replaces with bigwig_http_static --- src/bigwig_http_static.erl | 57 -------------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 src/bigwig_http_static.erl diff --git a/src/bigwig_http_static.erl b/src/bigwig_http_static.erl deleted file mode 100644 index 9f6f122..0000000 --- a/src/bigwig_http_static.erl +++ /dev/null @@ -1,57 +0,0 @@ -%% -%% show details on the VM, releases, apps, etc. -%% --module(bigwig_http_static). --behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/3]). - --export([html/1, css/1, js/1]). - --compile(export_all). - -init({tcp, http}, Req, []) -> - {ok, Req, undefined_state}; -init({tcp, http}, Req, OnlyFile) -> - {ok, Req, OnlyFile}. - -handle(Req, undefined_state = State) -> - {[_|Path], Req2} = cowboy_http_req:path(Req), % strip <<"static">> - send(Req2, Path, State); - -handle(Req, OnlyFile = State) -> - send(Req, OnlyFile, State). - -send(Req, PathBins, State) -> - Path = [ binary_to_list(P) || P <- PathBins ], - case file(filename:join(Path)) of - {ok, Body} -> - Headers = [{<<"Content-Type">>, <<"text/html">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), - {ok, Req2, State}; - _ -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"404'd">>, Req), - {ok, Req2, State} - end. - -terminate(_Reason, _Req, _State) -> - ok. - -html(Name) -> - type("html", Name). -css(Name) -> - type("css", Name). -js(Name) -> - type("js", Name). - -type(Type, Name) -> - file(filename:join([Type, Name ++ Type])). - -file(Path) -> - Priv = priv(), - file:read_file(filename:join(Priv, Path)). - -priv() -> - case code:priv_dir(bigwig) of - {error,_} -> "priv"; - Priv -> Priv - end. From c833400be9871444505cd76bc4ae64771961e881 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 01:02:00 +0800 Subject: [PATCH 182/405] adopt lager logging and let the index page work --- rebar.config | 11 +++++++---- src/bigwig_app.erl | 20 ++++++++++++++++++++ src/bigwig_sup.erl | 35 ++++++++++++++++++----------------- start-dev.sh | 2 +- sys.config | 11 +++++++++++ 5 files changed, 57 insertions(+), 22 deletions(-) diff --git a/rebar.config b/rebar.config index f468963..3a556c6 100644 --- a/rebar.config +++ b/rebar.config @@ -1,8 +1,11 @@ -{erl_opts, [debug_info, fail_on_warning]}. +{erl_opts, [debug_info, fail_on_warning, + {parse_transform, lager_transform }]}. + %{require_otp_vsn, "R14"}. {deps_dir, ["deps"]}. -{deps, [{cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, - {jsx, ".*", {git, "git://github.com/spawnfest/jsx.git", "master"}}, - {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}} +{deps, [ + {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, + {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, + {jsx, ".*", {git, "git://github.com/spawnfest/jsx.git", "master"}} ]}. diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 94492f4..3dcc05e 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -10,6 +10,26 @@ %% =================================================================== start(_StartType, _StartArgs) -> + + Dispatch = cowboy_router:compile([ + %% {URIHost, list({URIPath, Handler, Opts})} + {'_', [ + {"/", cowboy_static, {priv_file, bigwig, "html/index.html"}}, + {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", + [{mimetypes, cow_mimetypes, all}]}}, + {"/module", bigwig_http_module, []}, + {'_', bigwig_http_catchall, []} + ]} + ]), + + Port = 40829, + %% Name, NbAcceptors, TransOpts, ProtoOpts + cowboy:start_http(my_http_listener, 100, + [{port, Port}], + [{env, [{dispatch, Dispatch}]}] + ), + + lager:info("Bigwig listening on port ~B", [Port]), bigwig_sup:start_link(). stop(_State) -> diff --git a/src/bigwig_sup.erl b/src/bigwig_sup.erl index 6462e24..66802d0 100644 --- a/src/bigwig_sup.erl +++ b/src/bigwig_sup.erl @@ -9,7 +9,7 @@ -export([init/1]). %% Helper macro for declaring children of supervisor --define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). +%-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). %% =================================================================== %% API functions @@ -23,20 +23,21 @@ start_link() -> %% =================================================================== init([]) -> - Pubsub = ?CHILD(bigwig_pubsubhub, worker), - StatsSender = ?CHILD(bigwig_stats_sender, worker), - Http = ?CHILD(bigwig_http, worker), - ErrMon = ?CHILD(bigwig_error_handler_mon, worker), - Etop = ?CHILD(etop2, worker), - AppMon = ?CHILD(bigwig_appmon, worker), - - Specs = [ Pubsub, - StatsSender, - AppMon, - ErrMon, - Etop, - Http - ], - - {ok, { {one_for_one, 5, 10}, Specs} }. + %Pubsub = ?CHILD(bigwig_pubsubhub, worker), + %StatsSender = ?CHILD(bigwig_stats_sender, worker), + % Http = ?CHILD(bigwig_http, worker), + %ErrMon = ?CHILD(bigwig_error_handler_mon, worker), + %Etop = ?CHILD(etop2, worker), + %AppMon = ?CHILD(bigwig_appmon, worker), + + %Specs = [ Pubsub, + % StatsSender, + % AppMon, + % ErrMon, + % Etop, + % Http + % ], + + Procs = [], + {ok, {{one_for_one, 10, 10}, Procs}}. diff --git a/start-dev.sh b/start-dev.sh index f93109b..f5a7b36 100755 --- a/start-dev.sh +++ b/start-dev.sh @@ -2,4 +2,4 @@ cd `dirname $0` make mkdir -p log/sasl -erl -sname bigwig -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -config sys.config -s reloader -s bigwig +erl -name bigwig@localhost -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -config sys.config -s reloader -s bigwig diff --git a/sys.config b/sys.config index 5c518fc..c955e25 100644 --- a/sys.config +++ b/sys.config @@ -6,5 +6,16 @@ {error_logger_mf_dir, "log/sasl"}, % Log directory {error_logger_mf_maxbytes, 10485760}, % 10 MB max file size {error_logger_mf_maxfiles, 5} % 5 files max + ]}, + + {lager, [ + {async_threshold, 20}, + {async_threshold_window, 5}, + + {handlers, [ + {lager_console_backend, info}, + {lager_file_backend, [{file, "error.log"}, {level, error}]}, + {lager_file_backend, [{file, "console.log"}, {level, info}]} + ]} ]} ]. From 5ac0b6c04e5ea7bdef4b7ce0152917717b189fba Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 01:02:00 +0800 Subject: [PATCH 183/405] adopt lager logging and let the index page work --- src/bigwig_app.erl | 20 ++++++++++++++++++++ src/bigwig_sup.erl | 35 ++++++++++++++++++----------------- start-dev.sh | 2 +- sys.config | 11 +++++++++++ 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 94492f4..3dcc05e 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -10,6 +10,26 @@ %% =================================================================== start(_StartType, _StartArgs) -> + + Dispatch = cowboy_router:compile([ + %% {URIHost, list({URIPath, Handler, Opts})} + {'_', [ + {"/", cowboy_static, {priv_file, bigwig, "html/index.html"}}, + {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", + [{mimetypes, cow_mimetypes, all}]}}, + {"/module", bigwig_http_module, []}, + {'_', bigwig_http_catchall, []} + ]} + ]), + + Port = 40829, + %% Name, NbAcceptors, TransOpts, ProtoOpts + cowboy:start_http(my_http_listener, 100, + [{port, Port}], + [{env, [{dispatch, Dispatch}]}] + ), + + lager:info("Bigwig listening on port ~B", [Port]), bigwig_sup:start_link(). stop(_State) -> diff --git a/src/bigwig_sup.erl b/src/bigwig_sup.erl index 6462e24..66802d0 100644 --- a/src/bigwig_sup.erl +++ b/src/bigwig_sup.erl @@ -9,7 +9,7 @@ -export([init/1]). %% Helper macro for declaring children of supervisor --define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). +%-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). %% =================================================================== %% API functions @@ -23,20 +23,21 @@ start_link() -> %% =================================================================== init([]) -> - Pubsub = ?CHILD(bigwig_pubsubhub, worker), - StatsSender = ?CHILD(bigwig_stats_sender, worker), - Http = ?CHILD(bigwig_http, worker), - ErrMon = ?CHILD(bigwig_error_handler_mon, worker), - Etop = ?CHILD(etop2, worker), - AppMon = ?CHILD(bigwig_appmon, worker), - - Specs = [ Pubsub, - StatsSender, - AppMon, - ErrMon, - Etop, - Http - ], - - {ok, { {one_for_one, 5, 10}, Specs} }. + %Pubsub = ?CHILD(bigwig_pubsubhub, worker), + %StatsSender = ?CHILD(bigwig_stats_sender, worker), + % Http = ?CHILD(bigwig_http, worker), + %ErrMon = ?CHILD(bigwig_error_handler_mon, worker), + %Etop = ?CHILD(etop2, worker), + %AppMon = ?CHILD(bigwig_appmon, worker), + + %Specs = [ Pubsub, + % StatsSender, + % AppMon, + % ErrMon, + % Etop, + % Http + % ], + + Procs = [], + {ok, {{one_for_one, 10, 10}, Procs}}. diff --git a/start-dev.sh b/start-dev.sh index f93109b..f5a7b36 100755 --- a/start-dev.sh +++ b/start-dev.sh @@ -2,4 +2,4 @@ cd `dirname $0` make mkdir -p log/sasl -erl -sname bigwig -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -config sys.config -s reloader -s bigwig +erl -name bigwig@localhost -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -config sys.config -s reloader -s bigwig diff --git a/sys.config b/sys.config index 5c518fc..c955e25 100644 --- a/sys.config +++ b/sys.config @@ -6,5 +6,16 @@ {error_logger_mf_dir, "log/sasl"}, % Log directory {error_logger_mf_maxbytes, 10485760}, % 10 MB max file size {error_logger_mf_maxfiles, 5} % 5 files max + ]}, + + {lager, [ + {async_threshold, 20}, + {async_threshold_window, 5}, + + {handlers, [ + {lager_console_backend, info}, + {lager_file_backend, [{file, "error.log"}, {level, error}]}, + {lager_file_backend, [{file, "console.log"}, {level, info}]} + ]} ]} ]. From d33898cf05e72606642deaf6602ed004bcc56075 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 01:10:23 +0800 Subject: [PATCH 184/405] clean the code according to the latest cowboy api --- src/bigwig_http_catchall.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bigwig_http_catchall.erl b/src/bigwig_http_catchall.erl index c1d21a2..b2772e5 100644 --- a/src/bigwig_http_catchall.erl +++ b/src/bigwig_http_catchall.erl @@ -1,13 +1,14 @@ -module(bigwig_http_catchall). -behaviour(cowboy_http_handler). + -export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> - {ok, Req, undefined_state}. + {ok, Req, no_state}. handle(Req, State) -> Body = <<"

    404

    ">>, - {ok, Req2} = cowboy_http_req:reply(404, [], Body, Req), + {ok, Req2} = cowboy_req:reply(404, [], Body, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> From 3edfc532bae55ce74517b70dd190e2d5e2b0a740 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 01:10:23 +0800 Subject: [PATCH 185/405] clean the code according to the latest cowboy api --- src/bigwig_http_catchall.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bigwig_http_catchall.erl b/src/bigwig_http_catchall.erl index c1d21a2..b2772e5 100644 --- a/src/bigwig_http_catchall.erl +++ b/src/bigwig_http_catchall.erl @@ -1,13 +1,14 @@ -module(bigwig_http_catchall). -behaviour(cowboy_http_handler). + -export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> - {ok, Req, undefined_state}. + {ok, Req, no_state}. handle(Req, State) -> Body = <<"

    404

    ">>, - {ok, Req2} = cowboy_http_req:reply(404, [], Body, Req), + {ok, Req2} = cowboy_req:reply(404, [], Body, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> From b8c5ba776b33fa54bb341d3ede7b9829c18c4a88 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 09:43:26 +0800 Subject: [PATCH 186/405] refactor etop2 handler --- src/bigwig_app.erl | 6 +++++- src/bigwig_http_appmon.erl | 14 +++++++------- src/bigwig_http_etop2.erl | 25 ++++++++++++------------ src/bigwig_http_stats_stream.erl | 8 +++----- src/bigwig_sup.erl | 33 ++++++++++++++++---------------- 5 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 3dcc05e..9e67cc0 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -17,7 +17,11 @@ start(_StartType, _StartArgs) -> {"/", cowboy_static, {priv_file, bigwig, "html/index.html"}}, {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", [{mimetypes, cow_mimetypes, all}]}}, - {"/module", bigwig_http_module, []}, + {"/pid", bigwig_http_pid, []}, + {"/top", bigwig_http_etop2, []}, + {"/appmon", bigwig_http_appmon, []}, + {"/module", bigwig_http_module, []}, + {"/stats-stream", bigwig_http_stats_stream, []}, {'_', bigwig_http_catchall, []} ]} ]), diff --git a/src/bigwig_http_appmon.erl b/src/bigwig_http_appmon.erl index d907c85..2c5147a 100644 --- a/src/bigwig_http_appmon.erl +++ b/src/bigwig_http_appmon.erl @@ -10,14 +10,14 @@ -define(B2EA(B), (catch(list_to_existing_atom(binary_to_list(B))))). init({tcp, http}, Req, _Opts) -> - {ok, Req, undefined_state}. + {ok, Req, no_state}. handle(Req0, State) -> - {Path, Req} = cowboy_http_req:path(Req0), - {Method, Req1} = cowboy_http_req:method(Req), + {Path, Req} = cowboy_req:path(Req0), + {Method, Req1} = cowboy_req:method(Req), handle_path(Method, Path, Req1, State). -handle_path('GET', [<<"appmon">>, <<"_all">>], Req, State) -> +handle_path('GET', [<<"/appmon">>, <<"_all">>], Req, State) -> {ok, RawApps} = bigwig_appmon_info:node_apps(), Info = [{?P2B(Pid), @@ -26,7 +26,7 @@ handle_path('GET', [<<"appmon">>, <<"_all">>], Req, State) -> {ver, ?L2B(Ver)}]} || {Pid, _, {App, Desc, Ver}} <- RawApps], Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}; handle_path('GET', [<<"appmon">>, App0], Req, State) -> case ?B2EA(App0) of @@ -34,7 +34,7 @@ handle_path('GET', [<<"appmon">>, App0], Req, State) -> {ok, Info} = bigwig_appmon_info:calc_app_tree(App), Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}; _ -> not_found(Req, State) @@ -43,7 +43,7 @@ handle_path(_, _, Req, State) -> not_found(Req, State). not_found(Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), + {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> diff --git a/src/bigwig_http_etop2.erl b/src/bigwig_http_etop2.erl index 4ea98e8..f4fb90b 100644 --- a/src/bigwig_http_etop2.erl +++ b/src/bigwig_http_etop2.erl @@ -6,24 +6,25 @@ -export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> - {ok, Req, undefined_state}. + {ok, Req, no_state}. handle(Req0, State) -> - {Path, Req} = cowboy_http_req:path(Req0), - {Method, Req1} = cowboy_http_req:method(Req), + {Path, Req} = cowboy_req:path(Req0), + {Method, Req1} = cowboy_req:method(Req), + %io:format("==> ~p~n", [Path]), handle_path(Method, Path, Req1, State). -handle_path('GET', [<<"top">>], Req, State) -> +handle_path('GET', [<<"/top">>], Req, State) -> handle_get(Req, State); -handle_path('POST', [<<"top">>, <<"config">>, Key, Value], Req, State) -> +handle_path('POST', [<<"/top">>, <<"config">>, Key, Value], Req, State) -> handle_post_config(Key, Value, Req, State); -handle_path('POST', [<<"top">>, <<"node">>, Node], Req, State) -> +handle_path('POST', [<<"/top">>, <<"node">>, Node], Req, State) -> handle_post_node(fun to_node/1, Node, Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). not_found(Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), + {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> @@ -33,7 +34,7 @@ handle_get(Req, State) -> Headers = [{<<"Content-Type">>, <<"application/json">>}], Info = etop2:update(), Body = jsx:term_to_json(Info), - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_post_config(Key, Value, Req, State) -> @@ -48,16 +49,16 @@ handle_post_node(Get, Node, Req, State) -> post_config_response(Key, Value, Req, State) -> {ok, Req1} = case etop2:config(Key, Value) of - ok -> cowboy_http_req:reply(204, [], <<>>, Req); - Error -> cowboy_http_req:reply(403, [], Error, Req) + ok -> cowboy_req:reply(204, [], <<>>, Req); + Error -> cowboy_req:reply(403, [], Error, Req) end, {ok, Req1, State}. post_node_response(Node, Req, State) -> {ok, Req1} = case etop2:node(Node) of - ok -> cowboy_http_req:reply(204, [], <<>>, Req); - Error -> cowboy_http_req:reply(404, [], Error, Req) + ok -> cowboy_req:reply(204, [], <<>>, Req); + Error -> cowboy_req:reply(404, [], Error, Req) end, {ok, Req1, State}. diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index e6606ab..217dba6 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -1,17 +1,15 @@ %% Websocket connection that sends periodic stats about the VM %% -module(bigwig_http_stats_stream). --behaviour(cowboy_http_handler). -%-behaviour(cowboy_http_websocket_handler). -behaviour(cowboy_websocket_handler). -export([init/3, handle/2, terminate/3]). --export([websocket_init/3, websocket_handle/3, websocket_terminate/3, - websocket_info/3]). +-export([websocket_init/3, websocket_handle/3, + websocket_info/3, websocket_terminate/3]). init({tcp, http}, _Req, _Opts) -> - {upgrade, protocol, cowboy_http_websocket}. + {upgrade, protocol, cowboy_websocket}. handle(_Req, _State) -> exit(websockets_only). diff --git a/src/bigwig_sup.erl b/src/bigwig_sup.erl index 66802d0..00de163 100644 --- a/src/bigwig_sup.erl +++ b/src/bigwig_sup.erl @@ -9,7 +9,7 @@ -export([init/1]). %% Helper macro for declaring children of supervisor -%-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). +-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). %% =================================================================== %% API functions @@ -23,21 +23,20 @@ start_link() -> %% =================================================================== init([]) -> - %Pubsub = ?CHILD(bigwig_pubsubhub, worker), - %StatsSender = ?CHILD(bigwig_stats_sender, worker), + Pubsub = ?CHILD(bigwig_pubsubhub, worker), + StatsSender = ?CHILD(bigwig_stats_sender, worker), % Http = ?CHILD(bigwig_http, worker), - %ErrMon = ?CHILD(bigwig_error_handler_mon, worker), - %Etop = ?CHILD(etop2, worker), - %AppMon = ?CHILD(bigwig_appmon, worker), - - %Specs = [ Pubsub, - % StatsSender, - % AppMon, - % ErrMon, - % Etop, - % Http - % ], - - Procs = [], - {ok, {{one_for_one, 10, 10}, Procs}}. + ErrMon = ?CHILD(bigwig_error_handler_mon, worker), + Etop = ?CHILD(etop2, worker), + AppMon = ?CHILD(bigwig_appmon, worker), + + Specs = [ Pubsub, + StatsSender, + AppMon, + ErrMon, + Etop %, + % Http + ], + + {ok, { {one_for_one, 5, 10}, Specs} }. From 55cf9eab3b371757a670586ed03bc6c142ea7528 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 09:43:26 +0800 Subject: [PATCH 187/405] refactor etop2 handler --- src/bigwig_app.erl | 6 +++++- src/bigwig_http_appmon.erl | 14 +++++++------- src/bigwig_http_etop2.erl | 25 ++++++++++++------------ src/bigwig_http_stats_stream.erl | 8 +++----- src/bigwig_sup.erl | 33 ++++++++++++++++---------------- 5 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 3dcc05e..9e67cc0 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -17,7 +17,11 @@ start(_StartType, _StartArgs) -> {"/", cowboy_static, {priv_file, bigwig, "html/index.html"}}, {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", [{mimetypes, cow_mimetypes, all}]}}, - {"/module", bigwig_http_module, []}, + {"/pid", bigwig_http_pid, []}, + {"/top", bigwig_http_etop2, []}, + {"/appmon", bigwig_http_appmon, []}, + {"/module", bigwig_http_module, []}, + {"/stats-stream", bigwig_http_stats_stream, []}, {'_', bigwig_http_catchall, []} ]} ]), diff --git a/src/bigwig_http_appmon.erl b/src/bigwig_http_appmon.erl index d907c85..2c5147a 100644 --- a/src/bigwig_http_appmon.erl +++ b/src/bigwig_http_appmon.erl @@ -10,14 +10,14 @@ -define(B2EA(B), (catch(list_to_existing_atom(binary_to_list(B))))). init({tcp, http}, Req, _Opts) -> - {ok, Req, undefined_state}. + {ok, Req, no_state}. handle(Req0, State) -> - {Path, Req} = cowboy_http_req:path(Req0), - {Method, Req1} = cowboy_http_req:method(Req), + {Path, Req} = cowboy_req:path(Req0), + {Method, Req1} = cowboy_req:method(Req), handle_path(Method, Path, Req1, State). -handle_path('GET', [<<"appmon">>, <<"_all">>], Req, State) -> +handle_path('GET', [<<"/appmon">>, <<"_all">>], Req, State) -> {ok, RawApps} = bigwig_appmon_info:node_apps(), Info = [{?P2B(Pid), @@ -26,7 +26,7 @@ handle_path('GET', [<<"appmon">>, <<"_all">>], Req, State) -> {ver, ?L2B(Ver)}]} || {Pid, _, {App, Desc, Ver}} <- RawApps], Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}; handle_path('GET', [<<"appmon">>, App0], Req, State) -> case ?B2EA(App0) of @@ -34,7 +34,7 @@ handle_path('GET', [<<"appmon">>, App0], Req, State) -> {ok, Info} = bigwig_appmon_info:calc_app_tree(App), Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}; _ -> not_found(Req, State) @@ -43,7 +43,7 @@ handle_path(_, _, Req, State) -> not_found(Req, State). not_found(Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), + {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> diff --git a/src/bigwig_http_etop2.erl b/src/bigwig_http_etop2.erl index 4ea98e8..f4fb90b 100644 --- a/src/bigwig_http_etop2.erl +++ b/src/bigwig_http_etop2.erl @@ -6,24 +6,25 @@ -export([init/3, handle/2, terminate/3]). init({tcp, http}, Req, _Opts) -> - {ok, Req, undefined_state}. + {ok, Req, no_state}. handle(Req0, State) -> - {Path, Req} = cowboy_http_req:path(Req0), - {Method, Req1} = cowboy_http_req:method(Req), + {Path, Req} = cowboy_req:path(Req0), + {Method, Req1} = cowboy_req:method(Req), + %io:format("==> ~p~n", [Path]), handle_path(Method, Path, Req1, State). -handle_path('GET', [<<"top">>], Req, State) -> +handle_path('GET', [<<"/top">>], Req, State) -> handle_get(Req, State); -handle_path('POST', [<<"top">>, <<"config">>, Key, Value], Req, State) -> +handle_path('POST', [<<"/top">>, <<"config">>, Key, Value], Req, State) -> handle_post_config(Key, Value, Req, State); -handle_path('POST', [<<"top">>, <<"node">>, Node], Req, State) -> +handle_path('POST', [<<"/top">>, <<"node">>, Node], Req, State) -> handle_post_node(fun to_node/1, Node, Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). not_found(Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), + {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> @@ -33,7 +34,7 @@ handle_get(Req, State) -> Headers = [{<<"Content-Type">>, <<"application/json">>}], Info = etop2:update(), Body = jsx:term_to_json(Info), - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_post_config(Key, Value, Req, State) -> @@ -48,16 +49,16 @@ handle_post_node(Get, Node, Req, State) -> post_config_response(Key, Value, Req, State) -> {ok, Req1} = case etop2:config(Key, Value) of - ok -> cowboy_http_req:reply(204, [], <<>>, Req); - Error -> cowboy_http_req:reply(403, [], Error, Req) + ok -> cowboy_req:reply(204, [], <<>>, Req); + Error -> cowboy_req:reply(403, [], Error, Req) end, {ok, Req1, State}. post_node_response(Node, Req, State) -> {ok, Req1} = case etop2:node(Node) of - ok -> cowboy_http_req:reply(204, [], <<>>, Req); - Error -> cowboy_http_req:reply(404, [], Error, Req) + ok -> cowboy_req:reply(204, [], <<>>, Req); + Error -> cowboy_req:reply(404, [], Error, Req) end, {ok, Req1, State}. diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index e6606ab..217dba6 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -1,17 +1,15 @@ %% Websocket connection that sends periodic stats about the VM %% -module(bigwig_http_stats_stream). --behaviour(cowboy_http_handler). -%-behaviour(cowboy_http_websocket_handler). -behaviour(cowboy_websocket_handler). -export([init/3, handle/2, terminate/3]). --export([websocket_init/3, websocket_handle/3, websocket_terminate/3, - websocket_info/3]). +-export([websocket_init/3, websocket_handle/3, + websocket_info/3, websocket_terminate/3]). init({tcp, http}, _Req, _Opts) -> - {upgrade, protocol, cowboy_http_websocket}. + {upgrade, protocol, cowboy_websocket}. handle(_Req, _State) -> exit(websockets_only). diff --git a/src/bigwig_sup.erl b/src/bigwig_sup.erl index 66802d0..00de163 100644 --- a/src/bigwig_sup.erl +++ b/src/bigwig_sup.erl @@ -9,7 +9,7 @@ -export([init/1]). %% Helper macro for declaring children of supervisor -%-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). +-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). %% =================================================================== %% API functions @@ -23,21 +23,20 @@ start_link() -> %% =================================================================== init([]) -> - %Pubsub = ?CHILD(bigwig_pubsubhub, worker), - %StatsSender = ?CHILD(bigwig_stats_sender, worker), + Pubsub = ?CHILD(bigwig_pubsubhub, worker), + StatsSender = ?CHILD(bigwig_stats_sender, worker), % Http = ?CHILD(bigwig_http, worker), - %ErrMon = ?CHILD(bigwig_error_handler_mon, worker), - %Etop = ?CHILD(etop2, worker), - %AppMon = ?CHILD(bigwig_appmon, worker), - - %Specs = [ Pubsub, - % StatsSender, - % AppMon, - % ErrMon, - % Etop, - % Http - % ], - - Procs = [], - {ok, {{one_for_one, 10, 10}, Procs}}. + ErrMon = ?CHILD(bigwig_error_handler_mon, worker), + Etop = ?CHILD(etop2, worker), + AppMon = ?CHILD(bigwig_appmon, worker), + + Specs = [ Pubsub, + StatsSender, + AppMon, + ErrMon, + Etop %, + % Http + ], + + {ok, { {one_for_one, 5, 10}, Specs} }. From 01db47e46c6a984f5e885be7faa06b4557be333a Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 13:46:39 +0800 Subject: [PATCH 188/405] add otp_vsn limit --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 3a556c6..b91fc8e 100644 --- a/rebar.config +++ b/rebar.config @@ -1,7 +1,7 @@ {erl_opts, [debug_info, fail_on_warning, {parse_transform, lager_transform }]}. -%{require_otp_vsn, "R14"}. +{require_otp_vsn, "R16"}. {deps_dir, ["deps"]}. {deps, [ From a05763fcf61706f5b8c1735387dd93c295ed4510 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 7 Nov 2013 19:22:32 +0800 Subject: [PATCH 189/405] #5084 --- src/bigwig_appmon_info.erl | 3 ++- src/bigwig_http_stats_stream.erl | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bigwig_appmon_info.erl b/src/bigwig_appmon_info.erl index 0abfa76..f09671d 100644 --- a/src/bigwig_appmon_info.erl +++ b/src/bigwig_appmon_info.erl @@ -105,8 +105,9 @@ node_apps() -> {ok, lists:zf( fun(App) -> Name = element(1, App), + Value = element(2, App), case catch application_controller:get_master(Name) of - Pid when is_pid(Pid) -> {true, {Pid, Name, App}}; + Pid when is_pid(Pid) -> {true, {Name,Value}}; _ -> false end end, application:which_applications())}. diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index 217dba6..8c54287 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -28,7 +28,9 @@ websocket_info({bigwig, {bigwig_stats_sender, Stats}}, Req, State) -> {reply, {text, Reply}, Req, State}; websocket_info({bigwig, {bigwig_appmon, Stats}}, Req, State) -> - Reply = jsx:term_to_json([{appmon, Stats}]), + + io:format("stats is ~p",[Stats]), + Reply = jsx:term_to_json([{appmon, [Stats]}]), {reply, {text, Reply}, Req, State}; websocket_info({bigwig, _}, Req, State) -> From b5f0b090d8a49df5f17a5be1a4d671ebd9692e39 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 7 Nov 2013 19:22:32 +0800 Subject: [PATCH 190/405] #5084 --- src/bigwig_appmon_info.erl | 3 ++- src/bigwig_http_stats_stream.erl | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/bigwig_appmon_info.erl b/src/bigwig_appmon_info.erl index 0abfa76..f09671d 100644 --- a/src/bigwig_appmon_info.erl +++ b/src/bigwig_appmon_info.erl @@ -105,8 +105,9 @@ node_apps() -> {ok, lists:zf( fun(App) -> Name = element(1, App), + Value = element(2, App), case catch application_controller:get_master(Name) of - Pid when is_pid(Pid) -> {true, {Pid, Name, App}}; + Pid when is_pid(Pid) -> {true, {Name,Value}}; _ -> false end end, application:which_applications())}. diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index 217dba6..8c54287 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -28,7 +28,9 @@ websocket_info({bigwig, {bigwig_stats_sender, Stats}}, Req, State) -> {reply, {text, Reply}, Req, State}; websocket_info({bigwig, {bigwig_appmon, Stats}}, Req, State) -> - Reply = jsx:term_to_json([{appmon, Stats}]), + + io:format("stats is ~p",[Stats]), + Reply = jsx:term_to_json([{appmon, [Stats]}]), {reply, {text, Reply}, Req, State}; websocket_info({bigwig, _}, Req, State) -> From 9076429b8c990f8c5552b99c6b521e8c3a6eec9e Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 7 Nov 2013 19:27:33 +0800 Subject: [PATCH 191/405] #5084 remove the test code --- src/bigwig_http_stats_stream.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index 8c54287..cb2a962 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -28,8 +28,6 @@ websocket_info({bigwig, {bigwig_stats_sender, Stats}}, Req, State) -> {reply, {text, Reply}, Req, State}; websocket_info({bigwig, {bigwig_appmon, Stats}}, Req, State) -> - - io:format("stats is ~p",[Stats]), Reply = jsx:term_to_json([{appmon, [Stats]}]), {reply, {text, Reply}, Req, State}; From f7f6d85d17873a8371f242b92eedd2917f16f047 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 7 Nov 2013 19:27:33 +0800 Subject: [PATCH 192/405] #5084 remove the test code --- src/bigwig_http_stats_stream.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index 8c54287..cb2a962 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -28,8 +28,6 @@ websocket_info({bigwig, {bigwig_stats_sender, Stats}}, Req, State) -> {reply, {text, Reply}, Req, State}; websocket_info({bigwig, {bigwig_appmon, Stats}}, Req, State) -> - - io:format("stats is ~p",[Stats]), Reply = jsx:term_to_json([{appmon, [Stats]}]), {reply, {text, Reply}, Req, State}; From 84e1d6cc83e96657728d18a76e8717e3e6b3a820 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 23:30:13 +0800 Subject: [PATCH 193/405] let etop2 work --- include/observer_backend.hrl | 46 +++++----- src/bigwig_http_etop2.erl | 48 +++++----- src/etop2.erl | 165 +++++++++++++++++++---------------- 3 files changed, 139 insertions(+), 120 deletions(-) diff --git a/include/observer_backend.hrl b/include/observer_backend.hrl index 4be9bac..7d88e63 100644 --- a/include/observer_backend.hrl +++ b/include/observer_backend.hrl @@ -16,27 +16,27 @@ %% $Id$ %% --record(etop_info, - {now = {0, 0, 0}, - n_procs = 0, - wall_clock = {0, 0}, - runtime = {0, 0}, - run_queue = 0, - alloc_areas = [], - memi = [{total, 0}, - {processes, 0}, - {ets, 0}, - {atom, 0}, - {code, 0}, - {binary, 0}], - procinfo = [] - }). +-record(etop_info, { + now = {0, 0, 0}, + n_procs = 0, + wall_clock = {0, 0}, + runtime = {0, 0}, + run_queue = 0, + alloc_areas = [], + memi = [{total, 0}, + {processes, 0}, + {ets, 0}, + {atom, 0}, + {code, 0}, + {binary, 0}], + procinfo = [] + }). --record(etop_proc_info, - {pid, - mem=0, - reds=0, - name, - runtime=0, - cf, - mq=0}). +-record(etop_proc_info, { + pid, + mem=0, + reds=0, + name, + runtime=0, + cf, + mq=0}). diff --git a/src/bigwig_http_etop2.erl b/src/bigwig_http_etop2.erl index f4fb90b..c2705c9 100644 --- a/src/bigwig_http_etop2.erl +++ b/src/bigwig_http_etop2.erl @@ -11,14 +11,14 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), - %io:format("==> ~p~n", [Path]), + handle_path(Method, Path, Req1, State). -handle_path('GET', [<<"/top">>], Req, State) -> +handle_path(<<"GET">>, <<"/top">>, Req, State) -> handle_get(Req, State); -handle_path('POST', [<<"/top">>, <<"config">>, Key, Value], Req, State) -> +handle_path(<<"POST">>, [<<"/top">>, <<"config">>, Key, Value], Req, State) -> handle_post_config(Key, Value, Req, State); -handle_path('POST', [<<"/top">>, <<"node">>, Node], Req, State) -> +handle_path(<<"POST">>, [<<"/top">>, <<"node">>, Node], Req, State) -> handle_post_node(fun to_node/1, Node, Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). @@ -31,35 +31,35 @@ terminate(_Reason, _Req, _State) -> ok. handle_get(Req, State) -> - Headers = [{<<"Content-Type">>, <<"application/json">>}], - Info = etop2:update(), - Body = jsx:term_to_json(Info), - {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), - {ok, Req2, State}. + Headers = [{<<"Content-Type">>, <<"application/json">>}], + Info = etop2:update(), + Body = jsx:term_to_json(Info), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. handle_post_config(Key, Value, Req, State) -> - post_config_response(Key, Value, Req, State). + post_config_response(Key, Value, Req, State). handle_post_node(Get, Node, Req, State) -> - case catch(Get(Node)) of - A when is_atom(A) -> post_node_response(A, Req, State); - _ -> not_found(Req, State) - end. + case catch(Get(Node)) of + A when is_atom(A) -> post_node_response(A, Req, State); + _ -> not_found(Req, State) + end. post_config_response(Key, Value, Req, State) -> - {ok, Req1} = - case etop2:config(Key, Value) of - ok -> cowboy_req:reply(204, [], <<>>, Req); - Error -> cowboy_req:reply(403, [], Error, Req) - end, + {ok, Req1} = + case etop2:config(Key, Value) of + ok -> cowboy_req:reply(204, [], <<>>, Req); + Error -> cowboy_req:reply(403, [], Error, Req) + end, {ok, Req1, State}. post_node_response(Node, Req, State) -> - {ok, Req1} = - case etop2:node(Node) of - ok -> cowboy_req:reply(204, [], <<>>, Req); - Error -> cowboy_req:reply(404, [], Error, Req) - end, + {ok, Req1} = + case etop2:node(Node) of + ok -> cowboy_req:reply(204, [], <<>>, Req); + Error -> cowboy_req:reply(404, [], Error, Req) + end, {ok, Req1, State}. -spec to_node(binary()) -> node(). diff --git a/src/etop2.erl b/src/etop2.erl index 0c2b0e5..b581e1e 100644 --- a/src/etop2.erl +++ b/src/etop2.erl @@ -22,10 +22,19 @@ -behaviour(gen_server). %% API --export([start_link/0, start_link/1, update/0, node/1, config/2]). +-export([start_link/0, + start_link/1, + update/0, + node/1, + config/2]). %% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + code_change/3, + terminate/2]). %% Internal -export([loadinfo/1, meminfo/2]). @@ -133,35 +142,35 @@ terminate(_Reason, _) -> %% Internal functions check_runtime_tools_vsn(Node) -> - case rpc:call(Node,observer_backend,vsn,[]) of - {ok, Vsn} -> check_vsn(Vsn); - _ -> {error, "faulty version of runtime_tools on remote node"} + case rpc:call(Node, observer_backend, vsn, []) of + {ok, Vsn} -> check_vsn(Vsn); + _ -> {error, "faulty version of runtime_tools on remote node"} end. check_vsn(_Vsn) -> ok. -update(#opts{store=Store,node=Node,tracing=Tracing}=Opts) -> - Pid = spawn_link(Node,observer_backend,etop_collect,[self()]), - Info = receive {Pid,I} -> I - after 1000 -> exit(connection_lost) - end, - #etop_info{procinfo=ProcInfo} = Info, - ProcInfo1 = - if Tracing == on -> - PI=lists:map(fun(PI=#etop_proc_info{pid=P}) -> - case ets:lookup(Store,P) of - [{P,T}] -> PI#etop_proc_info{runtime=T}; - [] -> PI - end - end, - ProcInfo), - PI; - true -> - lists:map(fun(PI) -> PI#etop_proc_info{runtime='-'} end,ProcInfo) - end, - ProcInfo2 = sort(Opts,ProcInfo1), - Info#etop_info{procinfo=ProcInfo2}. - -sort(Opts,PI) -> +update(#opts{store=Store, node=Node, tracing=Tracing} = Opts) -> + Pid = spawn_link(Node, observer_backend, etop_collect, [self()]), + Info = receive {Pid,I} -> I + after 1000 -> exit(connection_lost) + end, + #etop_info{procinfo=ProcInfo} = Info, + ProcInfo1 = + if Tracing == on -> + PI=lists:map(fun(PI=#etop_proc_info{pid=P}) -> + case ets:lookup(Store,P) of + [{P,T}] -> PI#etop_proc_info{runtime=T}; + [] -> PI + end + end, + ProcInfo), + PI; + true -> + lists:map(fun(PI) -> PI#etop_proc_info{runtime='-'} end,ProcInfo) + end, + ProcInfo2 = sort(Opts,ProcInfo1), + Info#etop_info{procinfo=ProcInfo2}. + +sort(Opts, PI) -> Tag = get_tag(Opts#opts.sort), PI1 = if Opts#opts.accum -> PI; @@ -244,18 +253,27 @@ handle_args([_| R], C) -> handle_args([], C) -> C. -loadinfo(SysI) -> - #etop_info{n_procs = Procs, - run_queue = RQ, - now = Now, - wall_clock = {_, WC}, - runtime = {_, RT}} = SysI, - Cpu = try round(100*RT/WC) - catch _:_ -> 0 - end, - Clock = io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w", - tuple_to_list(element(2,calendar:now_to_datetime(Now)))), - {Cpu,Procs,RQ,Clock}. +loadinfo(#etop_info{} = SysI) -> + #etop_info{now = Now, + n_procs = Procs, + run_queue = RQ} = SysI, + + %#etop_info{n_procs = Procs, + %% run_queue = RQ, + % now = Now, + % wall_clock = {_, WC} %, + % runtime = {_, RT} + % } = SysI, + + + WC = 1, + RT = 0, + Cpu = try round(100 * RT / WC) + catch _:_ -> 0 + end, + Clock = io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w", + tuple_to_list(element(2,calendar:now_to_datetime(Now)))), + {Cpu,Procs,RQ,Clock}. meminfo(MemI, [Tag|Tags]) -> [round(get_mem(Tag, MemI)/1024)|meminfo(MemI, Tags)]; @@ -268,39 +286,40 @@ get_mem(Tag, MemI) -> end. update_json(Info, #opts{node=Node, accum=Accum}) -> - {Cpu,NProcs,RQ,Clock0} = loadinfo(Info), - Clock = iolist_to_binary(Clock0), - Header = - case Info#etop_info.memi of - undefined -> - [{<<"node">>, Node}, - {<<"clock">>, Clock}, - {<<"cpu">>, Cpu}, - {<<"nprocs">>, NProcs}, - {<<"runqueue">>, RQ}]; - Memi -> - [Tot,Procs,Atom,Bin,Code,Ets] = - etop2:meminfo(Memi, [total,processes,atom,binary,code,ets]), - [{<<"node">>, Node}, - {<<"clock">>, Clock}, - {<<"cpu">>, Cpu}, - {<<"tot">>, Tot}, - {<<"bin">>, Bin}, - {<<"nprocs">>, NProcs}, - {<<"procs">>, Procs}, - {<<"code">>, Code}, - {<<"runqueue">>, RQ}, - {<<"atom">>, Atom}, - {<<"ets">>, Ets}] - end, - Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], - NumPs = length(Ps), - [{<<"iTotalRecords">>, NumPs}, - {<<"accumulate">>, Accum}, - {<<"header">>, Header}, - {<<"iTotalDisplayRecords">>, NumPs}, - {<<"sColumns">>, <<"pid,name,time,reds,mem,mq,mfa">>}, - {<<"aaData">>, Ps}]. + {Cpu, NProcs, RQ, Clock0} = loadinfo(Info), + Clock = iolist_to_binary(Clock0), + Header = + case Info#etop_info.memi of + undefined -> + [{<<"node">>, atom_to_binary(Node, utf8)}, + {<<"clock">>, Clock}, + {<<"cpu">>, Cpu}, + {<<"nprocs">>, NProcs}, + {<<"runqueue">>, RQ}]; + Memi -> + [Tot,Procs,Atom,Bin,Code,Ets] = + etop2:meminfo(Memi, [total,processes,atom,binary,code,ets]), + [{<<"node">>, atom_to_binary(Node, utf8)}, + {<<"clock">>, Clock}, + {<<"cpu">>, Cpu}, + {<<"tot">>, Tot}, + {<<"bin">>, Bin}, + {<<"nprocs">>, NProcs}, + {<<"procs">>, Procs}, + {<<"code">>, Code}, + {<<"runqueue">>, RQ}, + {<<"atom">>, Atom}, + {<<"ets">>, Ets}] + end, + Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], + Ps2 = <<"">>, + NumPs = length(Ps), + [{<<"iTotalRecords">>, NumPs}, + {<<"accumulate">>, Accum}, + {<<"header">>, Header}, + {<<"iTotalDisplayRecords">>, NumPs}, + {<<"sColumns">>, <<"pid,name,time,reds,mem,mq,mfa">>}, + {<<"aaData">>, Ps2}]. name(Name) when is_atom(Name) -> Name; name({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> From eeb84d68de9c1c64615a47b75f43385305cf8c3d Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 7 Nov 2013 23:30:13 +0800 Subject: [PATCH 194/405] let etop2 work --- include/observer_backend.hrl | 46 +++++----- src/bigwig_http_etop2.erl | 48 +++++----- src/etop2.erl | 165 +++++++++++++++++++---------------- 3 files changed, 139 insertions(+), 120 deletions(-) diff --git a/include/observer_backend.hrl b/include/observer_backend.hrl index 4be9bac..7d88e63 100644 --- a/include/observer_backend.hrl +++ b/include/observer_backend.hrl @@ -16,27 +16,27 @@ %% $Id$ %% --record(etop_info, - {now = {0, 0, 0}, - n_procs = 0, - wall_clock = {0, 0}, - runtime = {0, 0}, - run_queue = 0, - alloc_areas = [], - memi = [{total, 0}, - {processes, 0}, - {ets, 0}, - {atom, 0}, - {code, 0}, - {binary, 0}], - procinfo = [] - }). +-record(etop_info, { + now = {0, 0, 0}, + n_procs = 0, + wall_clock = {0, 0}, + runtime = {0, 0}, + run_queue = 0, + alloc_areas = [], + memi = [{total, 0}, + {processes, 0}, + {ets, 0}, + {atom, 0}, + {code, 0}, + {binary, 0}], + procinfo = [] + }). --record(etop_proc_info, - {pid, - mem=0, - reds=0, - name, - runtime=0, - cf, - mq=0}). +-record(etop_proc_info, { + pid, + mem=0, + reds=0, + name, + runtime=0, + cf, + mq=0}). diff --git a/src/bigwig_http_etop2.erl b/src/bigwig_http_etop2.erl index f4fb90b..c2705c9 100644 --- a/src/bigwig_http_etop2.erl +++ b/src/bigwig_http_etop2.erl @@ -11,14 +11,14 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), - %io:format("==> ~p~n", [Path]), + handle_path(Method, Path, Req1, State). -handle_path('GET', [<<"/top">>], Req, State) -> +handle_path(<<"GET">>, <<"/top">>, Req, State) -> handle_get(Req, State); -handle_path('POST', [<<"/top">>, <<"config">>, Key, Value], Req, State) -> +handle_path(<<"POST">>, [<<"/top">>, <<"config">>, Key, Value], Req, State) -> handle_post_config(Key, Value, Req, State); -handle_path('POST', [<<"/top">>, <<"node">>, Node], Req, State) -> +handle_path(<<"POST">>, [<<"/top">>, <<"node">>, Node], Req, State) -> handle_post_node(fun to_node/1, Node, Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). @@ -31,35 +31,35 @@ terminate(_Reason, _Req, _State) -> ok. handle_get(Req, State) -> - Headers = [{<<"Content-Type">>, <<"application/json">>}], - Info = etop2:update(), - Body = jsx:term_to_json(Info), - {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), - {ok, Req2, State}. + Headers = [{<<"Content-Type">>, <<"application/json">>}], + Info = etop2:update(), + Body = jsx:term_to_json(Info), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. handle_post_config(Key, Value, Req, State) -> - post_config_response(Key, Value, Req, State). + post_config_response(Key, Value, Req, State). handle_post_node(Get, Node, Req, State) -> - case catch(Get(Node)) of - A when is_atom(A) -> post_node_response(A, Req, State); - _ -> not_found(Req, State) - end. + case catch(Get(Node)) of + A when is_atom(A) -> post_node_response(A, Req, State); + _ -> not_found(Req, State) + end. post_config_response(Key, Value, Req, State) -> - {ok, Req1} = - case etop2:config(Key, Value) of - ok -> cowboy_req:reply(204, [], <<>>, Req); - Error -> cowboy_req:reply(403, [], Error, Req) - end, + {ok, Req1} = + case etop2:config(Key, Value) of + ok -> cowboy_req:reply(204, [], <<>>, Req); + Error -> cowboy_req:reply(403, [], Error, Req) + end, {ok, Req1, State}. post_node_response(Node, Req, State) -> - {ok, Req1} = - case etop2:node(Node) of - ok -> cowboy_req:reply(204, [], <<>>, Req); - Error -> cowboy_req:reply(404, [], Error, Req) - end, + {ok, Req1} = + case etop2:node(Node) of + ok -> cowboy_req:reply(204, [], <<>>, Req); + Error -> cowboy_req:reply(404, [], Error, Req) + end, {ok, Req1, State}. -spec to_node(binary()) -> node(). diff --git a/src/etop2.erl b/src/etop2.erl index 0c2b0e5..b581e1e 100644 --- a/src/etop2.erl +++ b/src/etop2.erl @@ -22,10 +22,19 @@ -behaviour(gen_server). %% API --export([start_link/0, start_link/1, update/0, node/1, config/2]). +-export([start_link/0, + start_link/1, + update/0, + node/1, + config/2]). %% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + code_change/3, + terminate/2]). %% Internal -export([loadinfo/1, meminfo/2]). @@ -133,35 +142,35 @@ terminate(_Reason, _) -> %% Internal functions check_runtime_tools_vsn(Node) -> - case rpc:call(Node,observer_backend,vsn,[]) of - {ok, Vsn} -> check_vsn(Vsn); - _ -> {error, "faulty version of runtime_tools on remote node"} + case rpc:call(Node, observer_backend, vsn, []) of + {ok, Vsn} -> check_vsn(Vsn); + _ -> {error, "faulty version of runtime_tools on remote node"} end. check_vsn(_Vsn) -> ok. -update(#opts{store=Store,node=Node,tracing=Tracing}=Opts) -> - Pid = spawn_link(Node,observer_backend,etop_collect,[self()]), - Info = receive {Pid,I} -> I - after 1000 -> exit(connection_lost) - end, - #etop_info{procinfo=ProcInfo} = Info, - ProcInfo1 = - if Tracing == on -> - PI=lists:map(fun(PI=#etop_proc_info{pid=P}) -> - case ets:lookup(Store,P) of - [{P,T}] -> PI#etop_proc_info{runtime=T}; - [] -> PI - end - end, - ProcInfo), - PI; - true -> - lists:map(fun(PI) -> PI#etop_proc_info{runtime='-'} end,ProcInfo) - end, - ProcInfo2 = sort(Opts,ProcInfo1), - Info#etop_info{procinfo=ProcInfo2}. - -sort(Opts,PI) -> +update(#opts{store=Store, node=Node, tracing=Tracing} = Opts) -> + Pid = spawn_link(Node, observer_backend, etop_collect, [self()]), + Info = receive {Pid,I} -> I + after 1000 -> exit(connection_lost) + end, + #etop_info{procinfo=ProcInfo} = Info, + ProcInfo1 = + if Tracing == on -> + PI=lists:map(fun(PI=#etop_proc_info{pid=P}) -> + case ets:lookup(Store,P) of + [{P,T}] -> PI#etop_proc_info{runtime=T}; + [] -> PI + end + end, + ProcInfo), + PI; + true -> + lists:map(fun(PI) -> PI#etop_proc_info{runtime='-'} end,ProcInfo) + end, + ProcInfo2 = sort(Opts,ProcInfo1), + Info#etop_info{procinfo=ProcInfo2}. + +sort(Opts, PI) -> Tag = get_tag(Opts#opts.sort), PI1 = if Opts#opts.accum -> PI; @@ -244,18 +253,27 @@ handle_args([_| R], C) -> handle_args([], C) -> C. -loadinfo(SysI) -> - #etop_info{n_procs = Procs, - run_queue = RQ, - now = Now, - wall_clock = {_, WC}, - runtime = {_, RT}} = SysI, - Cpu = try round(100*RT/WC) - catch _:_ -> 0 - end, - Clock = io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w", - tuple_to_list(element(2,calendar:now_to_datetime(Now)))), - {Cpu,Procs,RQ,Clock}. +loadinfo(#etop_info{} = SysI) -> + #etop_info{now = Now, + n_procs = Procs, + run_queue = RQ} = SysI, + + %#etop_info{n_procs = Procs, + %% run_queue = RQ, + % now = Now, + % wall_clock = {_, WC} %, + % runtime = {_, RT} + % } = SysI, + + + WC = 1, + RT = 0, + Cpu = try round(100 * RT / WC) + catch _:_ -> 0 + end, + Clock = io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w", + tuple_to_list(element(2,calendar:now_to_datetime(Now)))), + {Cpu,Procs,RQ,Clock}. meminfo(MemI, [Tag|Tags]) -> [round(get_mem(Tag, MemI)/1024)|meminfo(MemI, Tags)]; @@ -268,39 +286,40 @@ get_mem(Tag, MemI) -> end. update_json(Info, #opts{node=Node, accum=Accum}) -> - {Cpu,NProcs,RQ,Clock0} = loadinfo(Info), - Clock = iolist_to_binary(Clock0), - Header = - case Info#etop_info.memi of - undefined -> - [{<<"node">>, Node}, - {<<"clock">>, Clock}, - {<<"cpu">>, Cpu}, - {<<"nprocs">>, NProcs}, - {<<"runqueue">>, RQ}]; - Memi -> - [Tot,Procs,Atom,Bin,Code,Ets] = - etop2:meminfo(Memi, [total,processes,atom,binary,code,ets]), - [{<<"node">>, Node}, - {<<"clock">>, Clock}, - {<<"cpu">>, Cpu}, - {<<"tot">>, Tot}, - {<<"bin">>, Bin}, - {<<"nprocs">>, NProcs}, - {<<"procs">>, Procs}, - {<<"code">>, Code}, - {<<"runqueue">>, RQ}, - {<<"atom">>, Atom}, - {<<"ets">>, Ets}] - end, - Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], - NumPs = length(Ps), - [{<<"iTotalRecords">>, NumPs}, - {<<"accumulate">>, Accum}, - {<<"header">>, Header}, - {<<"iTotalDisplayRecords">>, NumPs}, - {<<"sColumns">>, <<"pid,name,time,reds,mem,mq,mfa">>}, - {<<"aaData">>, Ps}]. + {Cpu, NProcs, RQ, Clock0} = loadinfo(Info), + Clock = iolist_to_binary(Clock0), + Header = + case Info#etop_info.memi of + undefined -> + [{<<"node">>, atom_to_binary(Node, utf8)}, + {<<"clock">>, Clock}, + {<<"cpu">>, Cpu}, + {<<"nprocs">>, NProcs}, + {<<"runqueue">>, RQ}]; + Memi -> + [Tot,Procs,Atom,Bin,Code,Ets] = + etop2:meminfo(Memi, [total,processes,atom,binary,code,ets]), + [{<<"node">>, atom_to_binary(Node, utf8)}, + {<<"clock">>, Clock}, + {<<"cpu">>, Cpu}, + {<<"tot">>, Tot}, + {<<"bin">>, Bin}, + {<<"nprocs">>, NProcs}, + {<<"procs">>, Procs}, + {<<"code">>, Code}, + {<<"runqueue">>, RQ}, + {<<"atom">>, Atom}, + {<<"ets">>, Ets}] + end, + Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], + Ps2 = <<"">>, + NumPs = length(Ps), + [{<<"iTotalRecords">>, NumPs}, + {<<"accumulate">>, Accum}, + {<<"header">>, Header}, + {<<"iTotalDisplayRecords">>, NumPs}, + {<<"sColumns">>, <<"pid,name,time,reds,mem,mq,mfa">>}, + {<<"aaData">>, Ps2}]. name(Name) when is_atom(Name) -> Name; name({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> From 8a87b865097b890330a9d1041328c0346b5fca5c Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Fri, 8 Nov 2013 01:00:23 +0800 Subject: [PATCH 195/405] let http vm work --- src/bigwig_app.erl | 1 + src/bigwig_http_appmon.erl | 1 + src/bigwig_http_vm.erl | 16 ++++++++++------ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 9e67cc0..8079ead 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -17,6 +17,7 @@ start(_StartType, _StartArgs) -> {"/", cowboy_static, {priv_file, bigwig, "html/index.html"}}, {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", [{mimetypes, cow_mimetypes, all}]}}, + {"/vm", bigwig_http_vm, []}, {"/pid", bigwig_http_pid, []}, {"/top", bigwig_http_etop2, []}, {"/appmon", bigwig_http_appmon, []}, diff --git a/src/bigwig_http_appmon.erl b/src/bigwig_http_appmon.erl index 2c5147a..05134fd 100644 --- a/src/bigwig_http_appmon.erl +++ b/src/bigwig_http_appmon.erl @@ -15,6 +15,7 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), + io:format("~p----> ~p~n", [Method, Path]), handle_path(Method, Path, Req1, State). handle_path('GET', [<<"/appmon">>, <<"_all">>], Req, State) -> diff --git a/src/bigwig_http_vm.erl b/src/bigwig_http_vm.erl index 9ab2310..d11b8a8 100644 --- a/src/bigwig_http_vm.erl +++ b/src/bigwig_http_vm.erl @@ -12,9 +12,12 @@ init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req, State) -> + io:format("===> vm ~p", [all()]), Body = jsx:term_to_json(all()), + + io:format("===> vm ~p", [Body]), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> @@ -23,8 +26,9 @@ terminate(_Reason, _Req, _State) -> all() -> [{system_info, system_info()}, - {releases, releases()}, - {applications,applications()}]. + {releases, releases()} + % {applications,applications()} + ]. %% Funs to load data about aspects of the system, mochiJSON formatted: @@ -52,9 +56,9 @@ releases() -> [ {list_to_binary(Name), [ - {version, list_to_binary(Version)}, - {status, list_to_binary(atom_to_list(Status))}, - {deps, FmtDeps(Deps)} + {version, list_to_binary(Version)}% , + % {status, list_to_binary(atom_to_list(Status))}% , + % {deps, FmtDeps(Deps)} ]} || {Name, Version, Deps, Status} <- Rels ]. From 1da699fe3d46d3b224708c353799f2d406eb02c0 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Fri, 8 Nov 2013 01:00:23 +0800 Subject: [PATCH 196/405] let http vm work --- src/bigwig_app.erl | 1 + src/bigwig_http_appmon.erl | 1 + src/bigwig_http_vm.erl | 16 ++++++++++------ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 9e67cc0..8079ead 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -17,6 +17,7 @@ start(_StartType, _StartArgs) -> {"/", cowboy_static, {priv_file, bigwig, "html/index.html"}}, {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", [{mimetypes, cow_mimetypes, all}]}}, + {"/vm", bigwig_http_vm, []}, {"/pid", bigwig_http_pid, []}, {"/top", bigwig_http_etop2, []}, {"/appmon", bigwig_http_appmon, []}, diff --git a/src/bigwig_http_appmon.erl b/src/bigwig_http_appmon.erl index 2c5147a..05134fd 100644 --- a/src/bigwig_http_appmon.erl +++ b/src/bigwig_http_appmon.erl @@ -15,6 +15,7 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), + io:format("~p----> ~p~n", [Method, Path]), handle_path(Method, Path, Req1, State). handle_path('GET', [<<"/appmon">>, <<"_all">>], Req, State) -> diff --git a/src/bigwig_http_vm.erl b/src/bigwig_http_vm.erl index 9ab2310..d11b8a8 100644 --- a/src/bigwig_http_vm.erl +++ b/src/bigwig_http_vm.erl @@ -12,9 +12,12 @@ init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req, State) -> + io:format("===> vm ~p", [all()]), Body = jsx:term_to_json(all()), + + io:format("===> vm ~p", [Body]), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> @@ -23,8 +26,9 @@ terminate(_Reason, _Req, _State) -> all() -> [{system_info, system_info()}, - {releases, releases()}, - {applications,applications()}]. + {releases, releases()} + % {applications,applications()} + ]. %% Funs to load data about aspects of the system, mochiJSON formatted: @@ -52,9 +56,9 @@ releases() -> [ {list_to_binary(Name), [ - {version, list_to_binary(Version)}, - {status, list_to_binary(atom_to_list(Status))}, - {deps, FmtDeps(Deps)} + {version, list_to_binary(Version)}% , + % {status, list_to_binary(atom_to_list(Status))}% , + % {deps, FmtDeps(Deps)} ]} || {Name, Version, Deps, Status} <- Rels ]. From 0c9612e0a5879c884c1530ddac8daf51a26d66ff Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 8 Nov 2013 18:42:36 +0800 Subject: [PATCH 197/405] let etop2 work --- src/etop2.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/etop2.erl b/src/etop2.erl index b581e1e..8ac5ba9 100644 --- a/src/etop2.erl +++ b/src/etop2.erl @@ -312,18 +312,19 @@ update_json(Info, #opts{node=Node, accum=Accum}) -> {<<"ets">>, Ets}] end, Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], - Ps2 = <<"">>, + %io:format("ps is ~p",[Ps]), + %Ps2 = <<"">>, NumPs = length(Ps), [{<<"iTotalRecords">>, NumPs}, {<<"accumulate">>, Accum}, {<<"header">>, Header}, {<<"iTotalDisplayRecords">>, NumPs}, {<<"sColumns">>, <<"pid,name,time,reds,mem,mq,mfa">>}, - {<<"aaData">>, Ps2}]. + {<<"aaData">>, Ps}]. -name(Name) when is_atom(Name) -> Name; +name(Name) when is_atom(Name) -> atom_to_binary(Name, utf8); name({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> - {M,F,A}. + list_to_binary([atom_to_list(M),":", atom_to_list(F),"/",integer_to_list(A)]). etop_proc_info_to_json( #etop_proc_info{pid=Pid, @@ -334,7 +335,8 @@ etop_proc_info_to_json( cf=MFA, mq=MQ}) -> - [Pid,name(Name),Time,Reds,Mem,MQ,name(MFA)]. + [ list_to_binary(pid_to_list(Pid)), name(Name),%atom_to_binary(Name,utf8), + atom_to_binary(Time,utf8), Reds, Mem, MQ, name(MFA) ]. %% Connect to a node and potentially set up tracing -spec connect(#opts{}) -> {ok, #opts{}} | {error, any()}. From ea509cf86833c30c41feb60aa73e20c4c05b2319 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 8 Nov 2013 18:42:36 +0800 Subject: [PATCH 198/405] let etop2 work --- src/etop2.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/etop2.erl b/src/etop2.erl index b581e1e..8ac5ba9 100644 --- a/src/etop2.erl +++ b/src/etop2.erl @@ -312,18 +312,19 @@ update_json(Info, #opts{node=Node, accum=Accum}) -> {<<"ets">>, Ets}] end, Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], - Ps2 = <<"">>, + %io:format("ps is ~p",[Ps]), + %Ps2 = <<"">>, NumPs = length(Ps), [{<<"iTotalRecords">>, NumPs}, {<<"accumulate">>, Accum}, {<<"header">>, Header}, {<<"iTotalDisplayRecords">>, NumPs}, {<<"sColumns">>, <<"pid,name,time,reds,mem,mq,mfa">>}, - {<<"aaData">>, Ps2}]. + {<<"aaData">>, Ps}]. -name(Name) when is_atom(Name) -> Name; +name(Name) when is_atom(Name) -> atom_to_binary(Name, utf8); name({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> - {M,F,A}. + list_to_binary([atom_to_list(M),":", atom_to_list(F),"/",integer_to_list(A)]). etop_proc_info_to_json( #etop_proc_info{pid=Pid, @@ -334,7 +335,8 @@ etop_proc_info_to_json( cf=MFA, mq=MQ}) -> - [Pid,name(Name),Time,Reds,Mem,MQ,name(MFA)]. + [ list_to_binary(pid_to_list(Pid)), name(Name),%atom_to_binary(Name,utf8), + atom_to_binary(Time,utf8), Reds, Mem, MQ, name(MFA) ]. %% Connect to a node and potentially set up tracing -spec connect(#opts{}) -> {ok, #opts{}} | {error, any()}. From 06ae9081fae55be190429b8a349f9fe9a8c826d8 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Sat, 9 Nov 2013 17:37:21 +0800 Subject: [PATCH 199/405] render.js(185),et.js(14)modify the HyperlinkProblem --- src/bigwig_app.erl | 6 ++++-- src/bigwig_http_module.erl | 12 ++++++------ src/bigwig_http_pid.erl | 18 +++++++++--------- src/bigwig_http_rb.erl | 10 +++++----- src/bigwig_http_rb_stream.erl | 4 ++-- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 8079ead..d0321c5 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -18,10 +18,12 @@ start(_StartType, _StartArgs) -> {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", [{mimetypes, cow_mimetypes, all}]}}, {"/vm", bigwig_http_vm, []}, - {"/pid", bigwig_http_pid, []}, + {"/rb/[...]", bigwig_http_rb, []}, + {"/rb/stream", bigwig_http_rb_stream, []}, + {"/pid", bigwig_http_pid, []}, {"/top", bigwig_http_etop2, []}, {"/appmon", bigwig_http_appmon, []}, - {"/module", bigwig_http_module, []}, + {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, {'_', bigwig_http_catchall, []} ]} diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index a0ddb89..f0f1f6f 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -9,18 +9,18 @@ init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req0, State) -> - {Path, Req} = cowboy_http_req:path(Req0), - {Method, Req1} = cowboy_http_req:method(Req), + {Path, Req} = cowboy_req:path(Req0), + {Method, Req1} = cowboy_req:method(Req), handle_path(Method, Path, Req1, State). handle_path('POST', [<<"module">>, Module], Req, State) -> - {Props, Req2} = cowboy_http_req:body_qs(Req), + {Props, Req2} = cowboy_req:body_qs(Req), case proplists:get_value(<<"reload">>, Props) of undefined -> not_found(Req2, State); <<"yes">> -> c:l(list_to_existing_atom(binary_to_list(Module))), - {ok, Req3} = cowboy_http_req:reply(200, [], <<"ok">>, Req2), + {ok, Req3} = cowboy_req:reply(200, [], <<"ok">>, Req2), {ok, Req3, State} end; handle_path('GET', [<<"module">>, Module], Req, State) -> @@ -32,7 +32,7 @@ handle_path(_, _, Req, State) -> not_found(Req, State). not_found(Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), + {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> @@ -69,5 +69,5 @@ to_module_info(Bin) -> json_response(Info, Req, State) -> Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index 3b451c8..dd6c870 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -9,8 +9,8 @@ init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req0, State) -> - {Path, Req} = cowboy_http_req:path(Req0), - {Method, Req1} = cowboy_http_req:method(Req), + {Path, Req} = cowboy_req:path(Req0), + {Method, Req1} = cowboy_req:method(Req), handle_path(Method, Path, Req1, State). handle_path('GET', [<<"pid">>, <<"global">>, Name], Req, State) -> @@ -27,7 +27,7 @@ handle_path(_, _, Req, State) -> not_found(Req, State). not_found(Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), + {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> @@ -50,13 +50,13 @@ handle_delete_pid(Get, Pid0, Req, State) -> case catch(Get(Pid0)) of Pid when is_pid(Pid) -> erlang:exit(Pid, kill), - {ok, Req2} = cowboy_http_req:reply(200, [], "killed", Req), + {ok, Req2} = cowboy_req:reply(200, [], "killed", Req), {ok, Req2, State}; _ -> not_found(Req, State) end. post_pid_response(Pid, Req, State) -> - {Res, Req1} = cowboy_http_req:body_qs(Req), + {Res, Req1} = cowboy_req:body_qs(Req), Headers = [{<<"Content-Type">>, <<"application/x-erlang-term">>}], {ok, Req2} = case lists:keyfind(<<"msg">>, 1, Res) of @@ -64,12 +64,12 @@ post_pid_response(Pid, Req, State) -> case catch(bigwig_util:parse_term(TermStr)) of {ok, Term} -> Body = io_lib:format("~p", [Pid ! Term]), - cowboy_http_req:reply(202, Headers, Body, Req1); + cowboy_req:reply(202, Headers, Body, Req1); _ -> - cowboy_http_req:reply(400, Headers, <<"{error, badarg}">>, Req1) + cowboy_req:reply(400, Headers, <<"{error, badarg}">>, Req1) end; _ -> - cowboy_http_req:reply(400, Headers, <<"{error, msg_required}">>, Req1) + cowboy_req:reply(400, Headers, <<"{error, msg_required}">>, Req1) end, {ok, Req2, State}. @@ -90,6 +90,6 @@ pid_response(Pid, Req, State) -> Info -> Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} end. diff --git a/src/bigwig_http_rb.erl b/src/bigwig_http_rb.erl index 818d0a0..f376c00 100644 --- a/src/bigwig_http_rb.erl +++ b/src/bigwig_http_rb.erl @@ -11,7 +11,7 @@ init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req, State) -> - {Path, Req2} = cowboy_http_req:path(Req), + {Path, Req2} = cowboy_req:path(Req), handle_path(Path, Req2, State). %% /rb/reports @@ -19,7 +19,7 @@ handle_path([<<"rb">>, <<"reports">>], Req0, State) -> {ReportFilter, Req} = make_report_filter_from_qs(Req0), Body = jsx:term_to_json(list_reports(ReportFilter)), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}; %% /rb/reports/123 @@ -27,11 +27,11 @@ handle_path([<<"rb">>, <<"reports">>, IdBin], Req, State) -> Id = list_to_integer(binary_to_list(IdBin)), Rep = bigwig_report_reader:load_number(Id), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, report_to_json(Rep), Req), + {ok, Req2} = cowboy_req:reply(200, Headers, report_to_json(Rep), Req), {ok, Req2, State}; handle_path(Path, Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], io_lib:format("Not found: ~p", [Path]), Req), %% FIXME injection + {ok, Req2} = cowboy_req:reply(404, [], io_lib:format("Not found: ~p", [Path]), Req), %% FIXME injection {ok, Req2, State}. @@ -62,7 +62,7 @@ format_report({Hash,_Type,_Pid,_Date,Rep,Str}) when is_list(Rep) -> %% Make a proplist to pass to make_filter, from the querstring make_report_filter_from_qs(Req0) -> %% A version of qs_val that url-decodes values (ie no %20 etc), and to_list - Qsval = fun(K,R) -> case cowboy_http_req:qs_val(K, R) of + Qsval = fun(K,R) -> case cowboy_req:qs_val(K, R) of {undefined, R2} -> {undefined, R2}; {ValEnc, R2} -> {bigwig_util:url_decode(ValEnc), R2} end diff --git a/src/bigwig_http_rb_stream.erl b/src/bigwig_http_rb_stream.erl index 96144e2..a5c1809 100644 --- a/src/bigwig_http_rb_stream.erl +++ b/src/bigwig_http_rb_stream.erl @@ -9,10 +9,10 @@ websocket_info/3]). init({tcp, http}, _Req, _Opts) -> - {upgrade, protocol, cowboy_http_websocket}. + {upgrade, protocol, cowboy_websocket}. handle(Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"Websockets only here pls">>, Req), + {ok, Req2} = cowboy_req:reply(404, [], <<"Websockets only here pls">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> From 3f131dc7ce3dbb123a79bd6381eb87333675274b Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Sat, 9 Nov 2013 17:37:21 +0800 Subject: [PATCH 200/405] render.js(185),et.js(14)modify the HyperlinkProblem --- src/bigwig_app.erl | 6 ++++-- src/bigwig_http_module.erl | 12 ++++++------ src/bigwig_http_pid.erl | 18 +++++++++--------- src/bigwig_http_rb.erl | 10 +++++----- src/bigwig_http_rb_stream.erl | 4 ++-- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 8079ead..d0321c5 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -18,10 +18,12 @@ start(_StartType, _StartArgs) -> {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", [{mimetypes, cow_mimetypes, all}]}}, {"/vm", bigwig_http_vm, []}, - {"/pid", bigwig_http_pid, []}, + {"/rb/[...]", bigwig_http_rb, []}, + {"/rb/stream", bigwig_http_rb_stream, []}, + {"/pid", bigwig_http_pid, []}, {"/top", bigwig_http_etop2, []}, {"/appmon", bigwig_http_appmon, []}, - {"/module", bigwig_http_module, []}, + {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, {'_', bigwig_http_catchall, []} ]} diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index a0ddb89..f0f1f6f 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -9,18 +9,18 @@ init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req0, State) -> - {Path, Req} = cowboy_http_req:path(Req0), - {Method, Req1} = cowboy_http_req:method(Req), + {Path, Req} = cowboy_req:path(Req0), + {Method, Req1} = cowboy_req:method(Req), handle_path(Method, Path, Req1, State). handle_path('POST', [<<"module">>, Module], Req, State) -> - {Props, Req2} = cowboy_http_req:body_qs(Req), + {Props, Req2} = cowboy_req:body_qs(Req), case proplists:get_value(<<"reload">>, Props) of undefined -> not_found(Req2, State); <<"yes">> -> c:l(list_to_existing_atom(binary_to_list(Module))), - {ok, Req3} = cowboy_http_req:reply(200, [], <<"ok">>, Req2), + {ok, Req3} = cowboy_req:reply(200, [], <<"ok">>, Req2), {ok, Req3, State} end; handle_path('GET', [<<"module">>, Module], Req, State) -> @@ -32,7 +32,7 @@ handle_path(_, _, Req, State) -> not_found(Req, State). not_found(Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), + {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> @@ -69,5 +69,5 @@ to_module_info(Bin) -> json_response(Info, Req, State) -> Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index 3b451c8..dd6c870 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -9,8 +9,8 @@ init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req0, State) -> - {Path, Req} = cowboy_http_req:path(Req0), - {Method, Req1} = cowboy_http_req:method(Req), + {Path, Req} = cowboy_req:path(Req0), + {Method, Req1} = cowboy_req:method(Req), handle_path(Method, Path, Req1, State). handle_path('GET', [<<"pid">>, <<"global">>, Name], Req, State) -> @@ -27,7 +27,7 @@ handle_path(_, _, Req, State) -> not_found(Req, State). not_found(Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    404

    ">>, Req), + {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> @@ -50,13 +50,13 @@ handle_delete_pid(Get, Pid0, Req, State) -> case catch(Get(Pid0)) of Pid when is_pid(Pid) -> erlang:exit(Pid, kill), - {ok, Req2} = cowboy_http_req:reply(200, [], "killed", Req), + {ok, Req2} = cowboy_req:reply(200, [], "killed", Req), {ok, Req2, State}; _ -> not_found(Req, State) end. post_pid_response(Pid, Req, State) -> - {Res, Req1} = cowboy_http_req:body_qs(Req), + {Res, Req1} = cowboy_req:body_qs(Req), Headers = [{<<"Content-Type">>, <<"application/x-erlang-term">>}], {ok, Req2} = case lists:keyfind(<<"msg">>, 1, Res) of @@ -64,12 +64,12 @@ post_pid_response(Pid, Req, State) -> case catch(bigwig_util:parse_term(TermStr)) of {ok, Term} -> Body = io_lib:format("~p", [Pid ! Term]), - cowboy_http_req:reply(202, Headers, Body, Req1); + cowboy_req:reply(202, Headers, Body, Req1); _ -> - cowboy_http_req:reply(400, Headers, <<"{error, badarg}">>, Req1) + cowboy_req:reply(400, Headers, <<"{error, badarg}">>, Req1) end; _ -> - cowboy_http_req:reply(400, Headers, <<"{error, msg_required}">>, Req1) + cowboy_req:reply(400, Headers, <<"{error, msg_required}">>, Req1) end, {ok, Req2, State}. @@ -90,6 +90,6 @@ pid_response(Pid, Req, State) -> Info -> Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} end. diff --git a/src/bigwig_http_rb.erl b/src/bigwig_http_rb.erl index 818d0a0..f376c00 100644 --- a/src/bigwig_http_rb.erl +++ b/src/bigwig_http_rb.erl @@ -11,7 +11,7 @@ init({tcp, http}, Req, _Opts) -> {ok, Req, undefined_state}. handle(Req, State) -> - {Path, Req2} = cowboy_http_req:path(Req), + {Path, Req2} = cowboy_req:path(Req), handle_path(Path, Req2, State). %% /rb/reports @@ -19,7 +19,7 @@ handle_path([<<"rb">>, <<"reports">>], Req0, State) -> {ReportFilter, Req} = make_report_filter_from_qs(Req0), Body = jsx:term_to_json(list_reports(ReportFilter)), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, Body, Req), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}; %% /rb/reports/123 @@ -27,11 +27,11 @@ handle_path([<<"rb">>, <<"reports">>, IdBin], Req, State) -> Id = list_to_integer(binary_to_list(IdBin)), Rep = bigwig_report_reader:load_number(Id), Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_http_req:reply(200, Headers, report_to_json(Rep), Req), + {ok, Req2} = cowboy_req:reply(200, Headers, report_to_json(Rep), Req), {ok, Req2, State}; handle_path(Path, Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], io_lib:format("Not found: ~p", [Path]), Req), %% FIXME injection + {ok, Req2} = cowboy_req:reply(404, [], io_lib:format("Not found: ~p", [Path]), Req), %% FIXME injection {ok, Req2, State}. @@ -62,7 +62,7 @@ format_report({Hash,_Type,_Pid,_Date,Rep,Str}) when is_list(Rep) -> %% Make a proplist to pass to make_filter, from the querstring make_report_filter_from_qs(Req0) -> %% A version of qs_val that url-decodes values (ie no %20 etc), and to_list - Qsval = fun(K,R) -> case cowboy_http_req:qs_val(K, R) of + Qsval = fun(K,R) -> case cowboy_req:qs_val(K, R) of {undefined, R2} -> {undefined, R2}; {ValEnc, R2} -> {bigwig_util:url_decode(ValEnc), R2} end diff --git a/src/bigwig_http_rb_stream.erl b/src/bigwig_http_rb_stream.erl index 96144e2..a5c1809 100644 --- a/src/bigwig_http_rb_stream.erl +++ b/src/bigwig_http_rb_stream.erl @@ -9,10 +9,10 @@ websocket_info/3]). init({tcp, http}, _Req, _Opts) -> - {upgrade, protocol, cowboy_http_websocket}. + {upgrade, protocol, cowboy_websocket}. handle(Req, State) -> - {ok, Req2} = cowboy_http_req:reply(404, [], <<"Websockets only here pls">>, Req), + {ok, Req2} = cowboy_req:reply(404, [], <<"Websockets only here pls">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> From bf1979f9cb71f6a8bb3f89bc6b6d2d973a83dace Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Sat, 9 Nov 2013 17:39:32 +0800 Subject: [PATCH 201/405] render.js(185),et.js(14)modify the HyperlinkProblem --- priv/js/et.js | 6 +++++- priv/js/renderer.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/priv/js/et.js b/priv/js/et.js index c24e09d..d6ee76d 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -12,7 +12,11 @@ $(document).ready(function() { // Use our RENDERER to format everything for(var i=0; i]/g, ''); + //json.aaData[i][0] = ($('
    <'+Pid+'>'))[0].outerHTML; + var Pid = json.aaData[i][0]; + json.aaData[i][0]= ($(''+Pid+''))[0].outerHTML; json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; json.aaData[i][6] = RENDERER.render_json_val(json.aaData[i][6])[0].outerHTML; } diff --git a/priv/js/renderer.js b/priv/js/renderer.js index d891515..93ec2e2 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -1,6 +1,6 @@ var RENDERER = (function() { var show_pid_dialog = function(Pid) { - var url = '/pid/' + Pid; + var url = '/pid/'+ Pid; $.ajax({ url: url, dataType: 'json', From 89ce4e9be2c42ad89e5bbd417df259490fa23554 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Sat, 9 Nov 2013 17:39:32 +0800 Subject: [PATCH 202/405] render.js(185),et.js(14)modify the HyperlinkProblem --- priv/js/et.js | 6 +++++- priv/js/renderer.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/priv/js/et.js b/priv/js/et.js index c24e09d..d6ee76d 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -12,7 +12,11 @@ $(document).ready(function() { // Use our RENDERER to format everything for(var i=0; i]/g, ''); + //json.aaData[i][0] = ($('<'+Pid+'>'))[0].outerHTML; + var Pid = json.aaData[i][0]; + json.aaData[i][0]= ($(''+Pid+''))[0].outerHTML; json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; json.aaData[i][6] = RENDERER.render_json_val(json.aaData[i][6])[0].outerHTML; } diff --git a/priv/js/renderer.js b/priv/js/renderer.js index d891515..93ec2e2 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -1,6 +1,6 @@ var RENDERER = (function() { var show_pid_dialog = function(Pid) { - var url = '/pid/' + Pid; + var url = '/pid/'+ Pid; $.ajax({ url: url, dataType: 'json', From 5ed467215aa23cde12c304ebe0ec522a6f21b8e1 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 11 Nov 2013 14:31:58 +0800 Subject: [PATCH 203/405] #5084 --- priv/js/et.js | 4 +++- priv/js/renderer.js | 46 ++++++++++++++++++++++++++++++++++++++++++++- src/bigwig_app.erl | 2 +- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/priv/js/et.js b/priv/js/et.js index d6ee76d..5dc5893 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -16,9 +16,11 @@ $(document).ready(function() { //var Pid = (json.aaData[i][0]).data.replace(/[<>]/g, ''); //json.aaData[i][0] = ($('<'+Pid+'>'))[0].outerHTML; var Pid = json.aaData[i][0]; + var Mfa = $('' + json.aaData[i][6] + ''); json.aaData[i][0]= ($(''+Pid+''))[0].outerHTML; json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; - json.aaData[i][6] = RENDERER.render_json_val(json.aaData[i][6])[0].outerHTML; + json.aaData[i][6] = (Mfa)[0].outerHTML; + //json.aaData[i][6] = RENDERER.render_json_val(json.aaData[i][6])[0].outerHTML; } var gs = ['node','clock','cpu','tot','bin','nprocs','procs','code','runqueue','atom','ets']; for (i = 0; i < gs.length; i++) { diff --git a/priv/js/renderer.js b/priv/js/renderer.js index 93ec2e2..ed730b9 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -43,7 +43,51 @@ var RENDERER = (function() { } } }); + }, + + undefined:function(json) + { + $('
    ') + .append( gen_pid_html(json) ) + .dialog({ + title: 'process_info(' + Pid + ')   ' + + '(json)', + width: 550, + buttons: { + "Close": function() { $(this).dialog("close"); }, + "Send Msg": function() { + var msg = prompt("Enter Erlang term to send to <" + Pid + ">"); + if(msg) + { + var thisdiag = $(this); + $.ajax({ + url:url, + type:'POST', + data: {msg: msg}, + success: function(resp){ + alert('Sent ok'); + } + }) + } + }, + "Kill" : function() { + if(confirm("Sure you want to kill <" + Pid + ">")) + { + var thisdiag = $(this); + $.ajax({ + url:url, + type:'DELETE', + success: function(resp){ + thisdiag.dialog("close"); + } + }) + } + } + } + }); + } + }); } @@ -210,7 +254,7 @@ var RENDERER = (function() { })(); $("body").delegate("a._pid", "click", function(e){ - var pid = $(this).text().replace(/[<>]/g,''); + var pid = $(this).text().replace(/[<>]/g,''); RENDERER.show_pid_dialog(pid); e.preventDefault(); }); diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index d0321c5..20da25c 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -20,7 +20,7 @@ start(_StartType, _StartArgs) -> {"/vm", bigwig_http_vm, []}, {"/rb/[...]", bigwig_http_rb, []}, {"/rb/stream", bigwig_http_rb_stream, []}, - {"/pid", bigwig_http_pid, []}, + {"/pid/[...]", bigwig_http_pid, []}, {"/top", bigwig_http_etop2, []}, {"/appmon", bigwig_http_appmon, []}, {"/module/[...]", bigwig_http_module, []}, From 2c08504746e0477f602bd5546ccba4dd7dbf22bf Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 11 Nov 2013 14:31:58 +0800 Subject: [PATCH 204/405] #5084 --- priv/js/et.js | 4 +++- priv/js/renderer.js | 46 ++++++++++++++++++++++++++++++++++++++++++++- src/bigwig_app.erl | 2 +- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/priv/js/et.js b/priv/js/et.js index d6ee76d..5dc5893 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -16,9 +16,11 @@ $(document).ready(function() { //var Pid = (json.aaData[i][0]).data.replace(/[<>]/g, ''); //json.aaData[i][0] = ($('<'+Pid+'>'))[0].outerHTML; var Pid = json.aaData[i][0]; + var Mfa = $('' + json.aaData[i][6] + ''); json.aaData[i][0]= ($(''+Pid+''))[0].outerHTML; json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; - json.aaData[i][6] = RENDERER.render_json_val(json.aaData[i][6])[0].outerHTML; + json.aaData[i][6] = (Mfa)[0].outerHTML; + //json.aaData[i][6] = RENDERER.render_json_val(json.aaData[i][6])[0].outerHTML; } var gs = ['node','clock','cpu','tot','bin','nprocs','procs','code','runqueue','atom','ets']; for (i = 0; i < gs.length; i++) { diff --git a/priv/js/renderer.js b/priv/js/renderer.js index 93ec2e2..ed730b9 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -43,7 +43,51 @@ var RENDERER = (function() { } } }); + }, + + undefined:function(json) + { + $('
    ') + .append( gen_pid_html(json) ) + .dialog({ + title: 'process_info(' + Pid + ')   ' + + '(json)', + width: 550, + buttons: { + "Close": function() { $(this).dialog("close"); }, + "Send Msg": function() { + var msg = prompt("Enter Erlang term to send to <" + Pid + ">"); + if(msg) + { + var thisdiag = $(this); + $.ajax({ + url:url, + type:'POST', + data: {msg: msg}, + success: function(resp){ + alert('Sent ok'); + } + }) + } + }, + "Kill" : function() { + if(confirm("Sure you want to kill <" + Pid + ">")) + { + var thisdiag = $(this); + $.ajax({ + url:url, + type:'DELETE', + success: function(resp){ + thisdiag.dialog("close"); + } + }) + } + } + } + }); + } + }); } @@ -210,7 +254,7 @@ var RENDERER = (function() { })(); $("body").delegate("a._pid", "click", function(e){ - var pid = $(this).text().replace(/[<>]/g,''); + var pid = $(this).text().replace(/[<>]/g,''); RENDERER.show_pid_dialog(pid); e.preventDefault(); }); diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index d0321c5..20da25c 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -20,7 +20,7 @@ start(_StartType, _StartArgs) -> {"/vm", bigwig_http_vm, []}, {"/rb/[...]", bigwig_http_rb, []}, {"/rb/stream", bigwig_http_rb_stream, []}, - {"/pid", bigwig_http_pid, []}, + {"/pid/[...]", bigwig_http_pid, []}, {"/top", bigwig_http_etop2, []}, {"/appmon", bigwig_http_appmon, []}, {"/module/[...]", bigwig_http_module, []}, From b87d5f95b24add9371c84335c80fd995273b8b38 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Mon, 11 Nov 2013 14:45:58 +0800 Subject: [PATCH 205/405] add http_pid path --- src/bigwig_app.erl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 20da25c..4740fca 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -17,15 +17,15 @@ start(_StartType, _StartArgs) -> {"/", cowboy_static, {priv_file, bigwig, "html/index.html"}}, {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", [{mimetypes, cow_mimetypes, all}]}}, - {"/vm", bigwig_http_vm, []}, - {"/rb/[...]", bigwig_http_rb, []}, - {"/rb/stream", bigwig_http_rb_stream, []}, - {"/pid/[...]", bigwig_http_pid, []}, - {"/top", bigwig_http_etop2, []}, - {"/appmon", bigwig_http_appmon, []}, - {"/module/[...]", bigwig_http_module, []}, - {"/stats-stream", bigwig_http_stats_stream, []}, - {'_', bigwig_http_catchall, []} + {"/vm", bigwig_http_vm, []}, + {"/rb/[...]", bigwig_http_rb, []}, + {"/rb/stream", bigwig_http_rb_stream, []}, + {"/pid/:pid", bigwig_http_pid, []}, + {"/top", bigwig_http_etop2, []}, + {"/appmon", bigwig_http_appmon, []}, + {"/module/:module", bigwig_http_module, []}, + {"/stats-stream", bigwig_http_stats_stream, []}, + {'_', bigwig_http_catchall, []} ]} ]), From 17aa7fd966c6e105f9ae830ab83b3aa17330e796 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Mon, 11 Nov 2013 14:45:58 +0800 Subject: [PATCH 206/405] add http_pid path --- src/bigwig_app.erl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 20da25c..4740fca 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -17,15 +17,15 @@ start(_StartType, _StartArgs) -> {"/", cowboy_static, {priv_file, bigwig, "html/index.html"}}, {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", [{mimetypes, cow_mimetypes, all}]}}, - {"/vm", bigwig_http_vm, []}, - {"/rb/[...]", bigwig_http_rb, []}, - {"/rb/stream", bigwig_http_rb_stream, []}, - {"/pid/[...]", bigwig_http_pid, []}, - {"/top", bigwig_http_etop2, []}, - {"/appmon", bigwig_http_appmon, []}, - {"/module/[...]", bigwig_http_module, []}, - {"/stats-stream", bigwig_http_stats_stream, []}, - {'_', bigwig_http_catchall, []} + {"/vm", bigwig_http_vm, []}, + {"/rb/[...]", bigwig_http_rb, []}, + {"/rb/stream", bigwig_http_rb_stream, []}, + {"/pid/:pid", bigwig_http_pid, []}, + {"/top", bigwig_http_etop2, []}, + {"/appmon", bigwig_http_appmon, []}, + {"/module/:module", bigwig_http_module, []}, + {"/stats-stream", bigwig_http_stats_stream, []}, + {'_', bigwig_http_catchall, []} ]} ]), From d5c6a1b61390195896288c98738c4c171f30c01f Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 11 Nov 2013 19:09:37 +0800 Subject: [PATCH 207/405] #5084 modify the href problem --- src/bigwig_app.erl | 4 ++-- src/bigwig_http_pid.erl | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 4740fca..8f5b4b8 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -20,10 +20,10 @@ start(_StartType, _StartArgs) -> {"/vm", bigwig_http_vm, []}, {"/rb/[...]", bigwig_http_rb, []}, {"/rb/stream", bigwig_http_rb_stream, []}, - {"/pid/:pid", bigwig_http_pid, []}, + {"/pid/[...]", bigwig_http_pid, []}, {"/top", bigwig_http_etop2, []}, {"/appmon", bigwig_http_appmon, []}, - {"/module/:module", bigwig_http_module, []}, + {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, {'_', bigwig_http_catchall, []} ]} diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index dd6c870..2ca155d 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -11,7 +11,8 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), - handle_path(Method, Path, Req1, State). + Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), + handle_path(binary_to_atom(Method,utf8), Path1, Req1, State). handle_path('GET', [<<"pid">>, <<"global">>, Name], Req, State) -> handle_get_pid(fun to_global_pid/1, Name, Req, State); @@ -85,11 +86,39 @@ to_global_pid(Name) -> global:whereis_name(list_to_existing_atom(binary_to_list(Name))). pid_response(Pid, Req, State) -> + % io:format("pid is ~p,info is ~p",[Pid,erlang:process_info(Pid)]), case erlang:process_info(Pid) of undefined -> not_found(Req, State); Info -> - Body = jsx:term_to_json(Info), + Info1=lists:map(fun(T) -> to_json(T) end,Info), + Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), + Info3=lists:keydelete(garbage_collection,1,Info2), + io:format("info is ~p",[Info3]), + Body = jsx:term_to_json(Info3), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} end. + +to_json(T) -> + case T of + {Key,Value}-> + case Value of + Value when is_atom(Value) == true -> + {Key,atom_to_binary(Value,utf8)}; + Value when is_pid(Value) == true -> + {Key,list_to_binary(pid_to_list(Value))}; + {M,F,A} -> {Key,list_to_binary(["{", atom_to_list(M), ":", atom_to_list(F), "/", integer_to_list(A), "}"])}; + [H|E]-> + {Key,[to_json(H),lists:map(fun(X) -> to_json(X) end,E)]}; + [] -> {Key,[]}; + _ -> {Key,Value} + end; + T when is_atom(T) == true -> + atom_to_binary(T,utf8); + T when is_pid(T) == true -> + list_to_binary(pid_to_list(T)); + T when is_port(T) == true -> + list_to_binary(erlang:port_to_list(T)) + end. + From 30bd8bb7082871c83282f52317ce8bbdcc2b9557 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 11 Nov 2013 19:09:37 +0800 Subject: [PATCH 208/405] #5084 modify the href problem --- src/bigwig_app.erl | 4 ++-- src/bigwig_http_pid.erl | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 4740fca..8f5b4b8 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -20,10 +20,10 @@ start(_StartType, _StartArgs) -> {"/vm", bigwig_http_vm, []}, {"/rb/[...]", bigwig_http_rb, []}, {"/rb/stream", bigwig_http_rb_stream, []}, - {"/pid/:pid", bigwig_http_pid, []}, + {"/pid/[...]", bigwig_http_pid, []}, {"/top", bigwig_http_etop2, []}, {"/appmon", bigwig_http_appmon, []}, - {"/module/:module", bigwig_http_module, []}, + {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, {'_', bigwig_http_catchall, []} ]} diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index dd6c870..2ca155d 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -11,7 +11,8 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), - handle_path(Method, Path, Req1, State). + Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), + handle_path(binary_to_atom(Method,utf8), Path1, Req1, State). handle_path('GET', [<<"pid">>, <<"global">>, Name], Req, State) -> handle_get_pid(fun to_global_pid/1, Name, Req, State); @@ -85,11 +86,39 @@ to_global_pid(Name) -> global:whereis_name(list_to_existing_atom(binary_to_list(Name))). pid_response(Pid, Req, State) -> + % io:format("pid is ~p,info is ~p",[Pid,erlang:process_info(Pid)]), case erlang:process_info(Pid) of undefined -> not_found(Req, State); Info -> - Body = jsx:term_to_json(Info), + Info1=lists:map(fun(T) -> to_json(T) end,Info), + Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), + Info3=lists:keydelete(garbage_collection,1,Info2), + io:format("info is ~p",[Info3]), + Body = jsx:term_to_json(Info3), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} end. + +to_json(T) -> + case T of + {Key,Value}-> + case Value of + Value when is_atom(Value) == true -> + {Key,atom_to_binary(Value,utf8)}; + Value when is_pid(Value) == true -> + {Key,list_to_binary(pid_to_list(Value))}; + {M,F,A} -> {Key,list_to_binary(["{", atom_to_list(M), ":", atom_to_list(F), "/", integer_to_list(A), "}"])}; + [H|E]-> + {Key,[to_json(H),lists:map(fun(X) -> to_json(X) end,E)]}; + [] -> {Key,[]}; + _ -> {Key,Value} + end; + T when is_atom(T) == true -> + atom_to_binary(T,utf8); + T when is_pid(T) == true -> + list_to_binary(pid_to_list(T)); + T when is_port(T) == true -> + list_to_binary(erlang:port_to_list(T)) + end. + From 56df3cbbf23721db5bf6407bb02b49fd6a6d9eb1 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 12 Nov 2013 15:06:51 +0800 Subject: [PATCH 209/405] let pid href work --- src/bigwig_http_pid.erl | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index 2ca155d..8dc61e4 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -12,17 +12,17 @@ handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), - handle_path(binary_to_atom(Method,utf8), Path1, Req1, State). + handle_path(Method, Path1, Req1, State). -handle_path('GET', [<<"pid">>, <<"global">>, Name], Req, State) -> +handle_path(<<"GET">>, [<<"pid">>, <<"global">>, Name], Req, State) -> handle_get_pid(fun to_global_pid/1, Name, Req, State); -handle_path('GET', [<<"pid">>, Pid], Req, State) -> +handle_path(<<"GET">>, [<<"pid">>, Pid], Req, State) -> handle_get_pid(fun to_pid/1, Pid, Req, State); -handle_path('POST', [<<"pid">>, <<"global">>, Name], Req, State) -> +handle_path(<<"POST">>, [<<"pid">>, <<"global">>, Name], Req, State) -> handle_post_pid(fun to_global_pid/1, Name, Req, State); -handle_path('POST', [<<"pid">>, Pid], Req, State) -> +handle_path(<<"POST">>, [<<"pid">>, Pid], Req, State) -> handle_post_pid(fun to_pid/1, Pid, Req, State); -handle_path('DELETE', [<<"pid">>, Pid], Req, State) -> +handle_path(<<"DELETE">>, [<<"pid">>, Pid], Req, State) -> handle_delete_pid(fun to_pid/1, Pid, Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). @@ -86,15 +86,12 @@ to_global_pid(Name) -> global:whereis_name(list_to_existing_atom(binary_to_list(Name))). pid_response(Pid, Req, State) -> - % io:format("pid is ~p,info is ~p",[Pid,erlang:process_info(Pid)]), case erlang:process_info(Pid) of undefined -> not_found(Req, State); Info -> Info1=lists:map(fun(T) -> to_json(T) end,Info), Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), - Info3=lists:keydelete(garbage_collection,1,Info2), - io:format("info is ~p",[Info3]), - Body = jsx:term_to_json(Info3), + Body = jsx:term_to_json(Info2), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} @@ -110,7 +107,7 @@ to_json(T) -> {Key,list_to_binary(pid_to_list(Value))}; {M,F,A} -> {Key,list_to_binary(["{", atom_to_list(M), ":", atom_to_list(F), "/", integer_to_list(A), "}"])}; [H|E]-> - {Key,[to_json(H),lists:map(fun(X) -> to_json(X) end,E)]}; + {Key,[to_json(H)]++lists:map(fun(X) -> to_json(X) end,E)}; [] -> {Key,[]}; _ -> {Key,Value} end; From 613f645063b55f5bd95937e5c064fd300be22302 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 12 Nov 2013 15:06:51 +0800 Subject: [PATCH 210/405] let pid href work --- src/bigwig_http_pid.erl | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index 2ca155d..8dc61e4 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -12,17 +12,17 @@ handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), - handle_path(binary_to_atom(Method,utf8), Path1, Req1, State). + handle_path(Method, Path1, Req1, State). -handle_path('GET', [<<"pid">>, <<"global">>, Name], Req, State) -> +handle_path(<<"GET">>, [<<"pid">>, <<"global">>, Name], Req, State) -> handle_get_pid(fun to_global_pid/1, Name, Req, State); -handle_path('GET', [<<"pid">>, Pid], Req, State) -> +handle_path(<<"GET">>, [<<"pid">>, Pid], Req, State) -> handle_get_pid(fun to_pid/1, Pid, Req, State); -handle_path('POST', [<<"pid">>, <<"global">>, Name], Req, State) -> +handle_path(<<"POST">>, [<<"pid">>, <<"global">>, Name], Req, State) -> handle_post_pid(fun to_global_pid/1, Name, Req, State); -handle_path('POST', [<<"pid">>, Pid], Req, State) -> +handle_path(<<"POST">>, [<<"pid">>, Pid], Req, State) -> handle_post_pid(fun to_pid/1, Pid, Req, State); -handle_path('DELETE', [<<"pid">>, Pid], Req, State) -> +handle_path(<<"DELETE">>, [<<"pid">>, Pid], Req, State) -> handle_delete_pid(fun to_pid/1, Pid, Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). @@ -86,15 +86,12 @@ to_global_pid(Name) -> global:whereis_name(list_to_existing_atom(binary_to_list(Name))). pid_response(Pid, Req, State) -> - % io:format("pid is ~p,info is ~p",[Pid,erlang:process_info(Pid)]), case erlang:process_info(Pid) of undefined -> not_found(Req, State); Info -> Info1=lists:map(fun(T) -> to_json(T) end,Info), Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), - Info3=lists:keydelete(garbage_collection,1,Info2), - io:format("info is ~p",[Info3]), - Body = jsx:term_to_json(Info3), + Body = jsx:term_to_json(Info2), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} @@ -110,7 +107,7 @@ to_json(T) -> {Key,list_to_binary(pid_to_list(Value))}; {M,F,A} -> {Key,list_to_binary(["{", atom_to_list(M), ":", atom_to_list(F), "/", integer_to_list(A), "}"])}; [H|E]-> - {Key,[to_json(H),lists:map(fun(X) -> to_json(X) end,E)]}; + {Key,[to_json(H)]++lists:map(fun(X) -> to_json(X) end,E)}; [] -> {Key,[]}; _ -> {Key,Value} end; From e7ee244b27a2d4982ec0b17885368661ffc886ed Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 12 Nov 2013 15:50:03 +0800 Subject: [PATCH 211/405] let moduleinfo href work --- src/bigwig_http_module.erl | 44 ++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index f0f1f6f..d7d9dcc 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -11,9 +11,10 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), - handle_path(Method, Path, Req1, State). + Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), + handle_path(Method, Path1, Req1, State). -handle_path('POST', [<<"module">>, Module], Req, State) -> +handle_path(<<"POST">>, [<<"module">>, Module], Req, State) -> {Props, Req2} = cowboy_req:body_qs(Req), case proplists:get_value(<<"reload">>, Props) of undefined -> @@ -23,10 +24,10 @@ handle_path('POST', [<<"module">>, Module], Req, State) -> {ok, Req3} = cowboy_req:reply(200, [], <<"ok">>, Req2), {ok, Req3, State} end; -handle_path('GET', [<<"module">>, Module], Req, State) -> +handle_path(<<"GET">>, [<<"module">>, Module], Req, State) -> case to_module_info(Module) of [_|_] = Info -> json_response(Info, Req, State); - _ -> not_found(Req, State) + _ -> not_found(Req, State) end; handle_path(_, _, Req, State) -> not_found(Req, State). @@ -52,9 +53,12 @@ to_module_info(Bin) -> case proplists:get_value(compile, ModInfo2) of L when is_list(L) -> case proplists:get_value(time, L) of - T when is_tuple(T) -> + T when is_tuple(T) -> + {Year,Month,Day,Hour,Minute,Sec}=T, proplists:delete(compile, L) ++ - [ {compile, [ {time, [{'_type',<<"date">>},{data,tuple_to_list(T)}]}]} + [ {compile, [ {time, [{'_type',<<"date">>},{data,list_to_binary([integer_to_list(Year) + ,integer_to_list(Month),integer_to_list(Day),integer_to_list(Hour), + integer_to_list(Minute),integer_to_list(Sec)])}]}]} | proplists:delete(time, L) ]; _ -> ModInfo2 @@ -62,7 +66,33 @@ to_module_info(Bin) -> _ -> ModInfo2 end, - ModInfo3. + ModInfo4 = + case proplists:get_value(time, ModInfo3) of + T1 when is_tuple(T1) -> + {Year1,Month1,Day1,Hour1,Minute1,Sec1}=T1, + Time = list_to_binary([integer_to_list(Year1) + ,integer_to_list(Month1),integer_to_list(Day1),integer_to_list(Hour1), + integer_to_list(Minute1),integer_to_list(Sec1)]), + [ {time, Time} | proplists:delete(time,ModInfo3)]; + _ -> + ModInfo3 + end, + ModInfo5 = + case proplists:get_value(source, ModInfo4) of + L1 when is_list(L1) -> + [ {source, list_to_binary(L1)} | proplists:delete(source,ModInfo4)]; + _ -> ModInfo4 + end, + ModInfo6 = + case proplists:get_value(version, ModInfo5) of + L2 when is_list(L2) -> + [ {version, list_to_binary(L2)} | proplists:delete(version,ModInfo5)]; + _ -> ModInfo5 + end, + ModInfo7=lists:ukeysort(1,ModInfo6), + ModInfo8= proplists:delete(options, ModInfo7), + + ModInfo8. From f35d756ab65f583ec088f53b5b0f6af09328abcc Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 12 Nov 2013 15:50:03 +0800 Subject: [PATCH 212/405] let moduleinfo href work --- src/bigwig_http_module.erl | 44 ++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index f0f1f6f..d7d9dcc 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -11,9 +11,10 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), - handle_path(Method, Path, Req1, State). + Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), + handle_path(Method, Path1, Req1, State). -handle_path('POST', [<<"module">>, Module], Req, State) -> +handle_path(<<"POST">>, [<<"module">>, Module], Req, State) -> {Props, Req2} = cowboy_req:body_qs(Req), case proplists:get_value(<<"reload">>, Props) of undefined -> @@ -23,10 +24,10 @@ handle_path('POST', [<<"module">>, Module], Req, State) -> {ok, Req3} = cowboy_req:reply(200, [], <<"ok">>, Req2), {ok, Req3, State} end; -handle_path('GET', [<<"module">>, Module], Req, State) -> +handle_path(<<"GET">>, [<<"module">>, Module], Req, State) -> case to_module_info(Module) of [_|_] = Info -> json_response(Info, Req, State); - _ -> not_found(Req, State) + _ -> not_found(Req, State) end; handle_path(_, _, Req, State) -> not_found(Req, State). @@ -52,9 +53,12 @@ to_module_info(Bin) -> case proplists:get_value(compile, ModInfo2) of L when is_list(L) -> case proplists:get_value(time, L) of - T when is_tuple(T) -> + T when is_tuple(T) -> + {Year,Month,Day,Hour,Minute,Sec}=T, proplists:delete(compile, L) ++ - [ {compile, [ {time, [{'_type',<<"date">>},{data,tuple_to_list(T)}]}]} + [ {compile, [ {time, [{'_type',<<"date">>},{data,list_to_binary([integer_to_list(Year) + ,integer_to_list(Month),integer_to_list(Day),integer_to_list(Hour), + integer_to_list(Minute),integer_to_list(Sec)])}]}]} | proplists:delete(time, L) ]; _ -> ModInfo2 @@ -62,7 +66,33 @@ to_module_info(Bin) -> _ -> ModInfo2 end, - ModInfo3. + ModInfo4 = + case proplists:get_value(time, ModInfo3) of + T1 when is_tuple(T1) -> + {Year1,Month1,Day1,Hour1,Minute1,Sec1}=T1, + Time = list_to_binary([integer_to_list(Year1) + ,integer_to_list(Month1),integer_to_list(Day1),integer_to_list(Hour1), + integer_to_list(Minute1),integer_to_list(Sec1)]), + [ {time, Time} | proplists:delete(time,ModInfo3)]; + _ -> + ModInfo3 + end, + ModInfo5 = + case proplists:get_value(source, ModInfo4) of + L1 when is_list(L1) -> + [ {source, list_to_binary(L1)} | proplists:delete(source,ModInfo4)]; + _ -> ModInfo4 + end, + ModInfo6 = + case proplists:get_value(version, ModInfo5) of + L2 when is_list(L2) -> + [ {version, list_to_binary(L2)} | proplists:delete(version,ModInfo5)]; + _ -> ModInfo5 + end, + ModInfo7=lists:ukeysort(1,ModInfo6), + ModInfo8= proplists:delete(options, ModInfo7), + + ModInfo8. From e73272908dc85e16b1a1350844543e442f995666 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 12 Nov 2013 15:51:10 +0800 Subject: [PATCH 213/405] let moduleinfo href work --- priv/js/et.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/priv/js/et.js b/priv/js/et.js index 5dc5893..aa9ebba 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -17,10 +17,18 @@ $(document).ready(function() { //json.aaData[i][0] = ($('<'+Pid+'>'))[0].outerHTML; var Pid = json.aaData[i][0]; var Mfa = $('' + json.aaData[i][6] + ''); + var Name = $('' + json.aaData[i][1] + ''); + json.aaData[i][0]= ($(''+Pid+''))[0].outerHTML; - json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; + var Str = json.aaData[i][1]; + if( Str.indexOf(":") == -1) { + json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; + } + else { + json.aaData[i][1] = (Name)[0].outerHTML; + } + json.aaData[i][6] = (Mfa)[0].outerHTML; - //json.aaData[i][6] = RENDERER.render_json_val(json.aaData[i][6])[0].outerHTML; } var gs = ['node','clock','cpu','tot','bin','nprocs','procs','code','runqueue','atom','ets']; for (i = 0; i < gs.length; i++) { From 5ea91e69669829457d104b673f0daec4ae6a57e5 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 12 Nov 2013 15:51:10 +0800 Subject: [PATCH 214/405] let moduleinfo href work --- priv/js/et.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/priv/js/et.js b/priv/js/et.js index 5dc5893..aa9ebba 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -17,10 +17,18 @@ $(document).ready(function() { //json.aaData[i][0] = ($('<'+Pid+'>'))[0].outerHTML; var Pid = json.aaData[i][0]; var Mfa = $('' + json.aaData[i][6] + ''); + var Name = $('' + json.aaData[i][1] + ''); + json.aaData[i][0]= ($(''+Pid+''))[0].outerHTML; - json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; + var Str = json.aaData[i][1]; + if( Str.indexOf(":") == -1) { + json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; + } + else { + json.aaData[i][1] = (Name)[0].outerHTML; + } + json.aaData[i][6] = (Mfa)[0].outerHTML; - //json.aaData[i][6] = RENDERER.render_json_val(json.aaData[i][6])[0].outerHTML; } var gs = ['node','clock','cpu','tot','bin','nprocs','procs','code','runqueue','atom','ets']; for (i = 0; i < gs.length; i++) { From 457958472e71402f54b917668dd3e6f09464e9ce Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 12 Nov 2013 18:37:48 +0800 Subject: [PATCH 215/405] let module info show normal --- src/bigwig_http_module.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index d7d9dcc..9c25838 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -56,9 +56,9 @@ to_module_info(Bin) -> T when is_tuple(T) -> {Year,Month,Day,Hour,Minute,Sec}=T, proplists:delete(compile, L) ++ - [ {compile, [ {time, [{'_type',<<"date">>},{data,list_to_binary([integer_to_list(Year) - ,integer_to_list(Month),integer_to_list(Day),integer_to_list(Hour), - integer_to_list(Minute),integer_to_list(Sec)])}]}]} + [ {compile, [ {time, [{'_type',<<"date">>},{data,[integer_to_binary(Year) + ,integer_to_binary(Month),integer_to_binary(Day),integer_to_binary(Hour), + integer_to_binary(Minute),integer_to_binary(Sec)]}]}]} | proplists:delete(time, L) ]; _ -> ModInfo2 From 1b3a82dc493fc1ec1308fa2b2cf54a104f155bd3 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 12 Nov 2013 18:37:48 +0800 Subject: [PATCH 216/405] let module info show normal --- src/bigwig_http_module.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index d7d9dcc..9c25838 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -56,9 +56,9 @@ to_module_info(Bin) -> T when is_tuple(T) -> {Year,Month,Day,Hour,Minute,Sec}=T, proplists:delete(compile, L) ++ - [ {compile, [ {time, [{'_type',<<"date">>},{data,list_to_binary([integer_to_list(Year) - ,integer_to_list(Month),integer_to_list(Day),integer_to_list(Hour), - integer_to_list(Minute),integer_to_list(Sec)])}]}]} + [ {compile, [ {time, [{'_type',<<"date">>},{data,[integer_to_binary(Year) + ,integer_to_binary(Month),integer_to_binary(Day),integer_to_binary(Hour), + integer_to_binary(Minute),integer_to_binary(Sec)]}]}]} | proplists:delete(time, L) ]; _ -> ModInfo2 From 39f8cefc10417cb7d26a0536a9d841398a8d92be Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 14 Nov 2013 15:37:41 +0800 Subject: [PATCH 217/405] let report work --- rebar.config | 2 +- src/bigwig_http_rb.erl | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index b91fc8e..75f5ea3 100644 --- a/rebar.config +++ b/rebar.config @@ -7,5 +7,5 @@ {deps, [ {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, - {jsx, ".*", {git, "git://github.com/spawnfest/jsx.git", "master"}} + {jsx, ".*", {git, "git://github.com/yuzhaoren/jsx.git", "master"}} ]}. diff --git a/src/bigwig_http_rb.erl b/src/bigwig_http_rb.erl index f376c00..d3eebae 100644 --- a/src/bigwig_http_rb.erl +++ b/src/bigwig_http_rb.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_rb). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/3]). +-export([init/3, handle/2, terminate/3,list_reports/1]). init({tcp, http}, Req, _Opts) -> bigwig_report_reader:start(), %% will only be started once anyway, registered name @@ -12,7 +12,8 @@ init({tcp, http}, Req, _Opts) -> handle(Req, State) -> {Path, Req2} = cowboy_req:path(Req), - handle_path(Path, Req2, State). + Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), + handle_path(Path1, Req2, State). %% /rb/reports handle_path([<<"rb">>, <<"reports">>], Req0, State) -> From 34ee630b11b512a700c2eaa012fae75196b564c6 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 14 Nov 2013 15:37:41 +0800 Subject: [PATCH 218/405] let report work --- src/bigwig_http_rb.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bigwig_http_rb.erl b/src/bigwig_http_rb.erl index f376c00..d3eebae 100644 --- a/src/bigwig_http_rb.erl +++ b/src/bigwig_http_rb.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_rb). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/3]). +-export([init/3, handle/2, terminate/3,list_reports/1]). init({tcp, http}, Req, _Opts) -> bigwig_report_reader:start(), %% will only be started once anyway, registered name @@ -12,7 +12,8 @@ init({tcp, http}, Req, _Opts) -> handle(Req, State) -> {Path, Req2} = cowboy_req:path(Req), - handle_path(Path, Req2, State). + Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), + handle_path(Path1, Req2, State). %% /rb/reports handle_path([<<"rb">>, <<"reports">>], Req0, State) -> From e26aa82dee4d37fbb6608116dec3f35079da3d98 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 14 Nov 2013 15:47:06 +0800 Subject: [PATCH 219/405] let application work --- src/bigwig_app.erl | 2 +- src/bigwig_http_appmon.erl | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 8f5b4b8..a88512d 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -22,7 +22,7 @@ start(_StartType, _StartArgs) -> {"/rb/stream", bigwig_http_rb_stream, []}, {"/pid/[...]", bigwig_http_pid, []}, {"/top", bigwig_http_etop2, []}, - {"/appmon", bigwig_http_appmon, []}, + {"/appmon/[...]", bigwig_http_appmon, []}, {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, {'_', bigwig_http_catchall, []} diff --git a/src/bigwig_http_appmon.erl b/src/bigwig_http_appmon.erl index 05134fd..58b2ccb 100644 --- a/src/bigwig_http_appmon.erl +++ b/src/bigwig_http_appmon.erl @@ -15,21 +15,23 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), - io:format("~p----> ~p~n", [Method, Path]), - handle_path(Method, Path, Req1, State). + Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), + handle_path(Method, Path1, Req1, State). -handle_path('GET', [<<"/appmon">>, <<"_all">>], Req, State) -> +handle_path(<<"GET">>, [<<"appmon">>], Req, State) -> {ok, RawApps} = bigwig_appmon_info:node_apps(), Info = [{?P2B(Pid), [{name, App}, {desc, ?L2B(Desc)}, {ver, ?L2B(Ver)}]} || {Pid, _, {App, Desc, Ver}} <- RawApps], + io:format("Info is ~p", [Info]), Body = jsx:term_to_json(Info), + io:format("Body is ~p", [Body]), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}; -handle_path('GET', [<<"appmon">>, App0], Req, State) -> +handle_path(<<"GET">>, [<<"appmon">>, App0], Req, State) -> case ?B2EA(App0) of App when is_atom(App) -> {ok, Info} = bigwig_appmon_info:calc_app_tree(App), From 4a9a0440c9188758ff004db641f16adec75098fe Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 14 Nov 2013 15:47:06 +0800 Subject: [PATCH 220/405] let application work --- src/bigwig_app.erl | 2 +- src/bigwig_http_appmon.erl | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 8f5b4b8..a88512d 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -22,7 +22,7 @@ start(_StartType, _StartArgs) -> {"/rb/stream", bigwig_http_rb_stream, []}, {"/pid/[...]", bigwig_http_pid, []}, {"/top", bigwig_http_etop2, []}, - {"/appmon", bigwig_http_appmon, []}, + {"/appmon/[...]", bigwig_http_appmon, []}, {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, {'_', bigwig_http_catchall, []} diff --git a/src/bigwig_http_appmon.erl b/src/bigwig_http_appmon.erl index 05134fd..58b2ccb 100644 --- a/src/bigwig_http_appmon.erl +++ b/src/bigwig_http_appmon.erl @@ -15,21 +15,23 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), - io:format("~p----> ~p~n", [Method, Path]), - handle_path(Method, Path, Req1, State). + Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), + handle_path(Method, Path1, Req1, State). -handle_path('GET', [<<"/appmon">>, <<"_all">>], Req, State) -> +handle_path(<<"GET">>, [<<"appmon">>], Req, State) -> {ok, RawApps} = bigwig_appmon_info:node_apps(), Info = [{?P2B(Pid), [{name, App}, {desc, ?L2B(Desc)}, {ver, ?L2B(Ver)}]} || {Pid, _, {App, Desc, Ver}} <- RawApps], + io:format("Info is ~p", [Info]), Body = jsx:term_to_json(Info), + io:format("Body is ~p", [Body]), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}; -handle_path('GET', [<<"appmon">>, App0], Req, State) -> +handle_path(<<"GET">>, [<<"appmon">>, App0], Req, State) -> case ?B2EA(App0) of App when is_atom(App) -> {ok, Info} = bigwig_appmon_info:calc_app_tree(App), From b83e1df02ded84624de99e160511f600f3157365 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 14 Nov 2013 16:06:04 +0800 Subject: [PATCH 221/405] remove old functions --- src/bigwig_appmon_info.erl | 5 ++-- src/bigwig_http_pid.erl | 48 +++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/bigwig_appmon_info.erl b/src/bigwig_appmon_info.erl index f09671d..ccb334a 100644 --- a/src/bigwig_appmon_info.erl +++ b/src/bigwig_appmon_info.erl @@ -105,9 +105,10 @@ node_apps() -> {ok, lists:zf( fun(App) -> Name = element(1, App), - Value = element(2, App), + %Name = element(1, App), + %Value = element(2, App), case catch application_controller:get_master(Name) of - Pid when is_pid(Pid) -> {true, {Name,Value}}; + Pid when is_pid(Pid) -> {true, {Pid, Name, App}};%{Name,Value}}; _ -> false end end, application:which_applications())}. diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index 8dc61e4..81ee80b 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -89,33 +89,33 @@ pid_response(Pid, Req, State) -> case erlang:process_info(Pid) of undefined -> not_found(Req, State); Info -> - Info1=lists:map(fun(T) -> to_json(T) end,Info), - Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), - Body = jsx:term_to_json(Info2), + % Info1=lists:map(fun(T) -> to_json(T) end,Info), + % Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), + Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} end. -to_json(T) -> - case T of - {Key,Value}-> - case Value of - Value when is_atom(Value) == true -> - {Key,atom_to_binary(Value,utf8)}; - Value when is_pid(Value) == true -> - {Key,list_to_binary(pid_to_list(Value))}; - {M,F,A} -> {Key,list_to_binary(["{", atom_to_list(M), ":", atom_to_list(F), "/", integer_to_list(A), "}"])}; - [H|E]-> - {Key,[to_json(H)]++lists:map(fun(X) -> to_json(X) end,E)}; - [] -> {Key,[]}; - _ -> {Key,Value} - end; - T when is_atom(T) == true -> - atom_to_binary(T,utf8); - T when is_pid(T) == true -> - list_to_binary(pid_to_list(T)); - T when is_port(T) == true -> - list_to_binary(erlang:port_to_list(T)) - end. +%to_json(T) -> +% case T of +% {Key,Value}-> +% case Value of +% Value when is_atom(Value) == true -> +% {Key,atom_to_binary(Value,utf8)}; +% Value when is_pid(Value) == true -> +% {Key,list_to_binary(pid_to_list(Value))}; +% {M,F,A} -> {Key,list_to_binary(["{", atom_to_list(M), ":", atom_to_list(F), "/", integer_to_list(A), "}"])}; +% [H|E]-> +% {Key,[to_json(H)]++lists:map(fun(X) -> to_json(X) end,E)}; +% [] -> {Key,[]}; +% _ -> {Key,Value} +% end; +% T when is_atom(T) == true -> + % atom_to_binary(T,utf8); + % T when is_pid(T) == true -> + % list_to_binary(pid_to_list(T)); + % T when is_port(T) == true -> + % list_to_binary(erlang:port_to_list(T)) + % end. From 5f693a303297d2dc9c8873787749292cadd417fe Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 14 Nov 2013 16:06:04 +0800 Subject: [PATCH 222/405] remove old functions --- src/bigwig_appmon_info.erl | 5 ++-- src/bigwig_http_pid.erl | 48 +++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/bigwig_appmon_info.erl b/src/bigwig_appmon_info.erl index f09671d..ccb334a 100644 --- a/src/bigwig_appmon_info.erl +++ b/src/bigwig_appmon_info.erl @@ -105,9 +105,10 @@ node_apps() -> {ok, lists:zf( fun(App) -> Name = element(1, App), - Value = element(2, App), + %Name = element(1, App), + %Value = element(2, App), case catch application_controller:get_master(Name) of - Pid when is_pid(Pid) -> {true, {Name,Value}}; + Pid when is_pid(Pid) -> {true, {Pid, Name, App}};%{Name,Value}}; _ -> false end end, application:which_applications())}. diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index 8dc61e4..81ee80b 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -89,33 +89,33 @@ pid_response(Pid, Req, State) -> case erlang:process_info(Pid) of undefined -> not_found(Req, State); Info -> - Info1=lists:map(fun(T) -> to_json(T) end,Info), - Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), - Body = jsx:term_to_json(Info2), + % Info1=lists:map(fun(T) -> to_json(T) end,Info), + % Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), + Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} end. -to_json(T) -> - case T of - {Key,Value}-> - case Value of - Value when is_atom(Value) == true -> - {Key,atom_to_binary(Value,utf8)}; - Value when is_pid(Value) == true -> - {Key,list_to_binary(pid_to_list(Value))}; - {M,F,A} -> {Key,list_to_binary(["{", atom_to_list(M), ":", atom_to_list(F), "/", integer_to_list(A), "}"])}; - [H|E]-> - {Key,[to_json(H)]++lists:map(fun(X) -> to_json(X) end,E)}; - [] -> {Key,[]}; - _ -> {Key,Value} - end; - T when is_atom(T) == true -> - atom_to_binary(T,utf8); - T when is_pid(T) == true -> - list_to_binary(pid_to_list(T)); - T when is_port(T) == true -> - list_to_binary(erlang:port_to_list(T)) - end. +%to_json(T) -> +% case T of +% {Key,Value}-> +% case Value of +% Value when is_atom(Value) == true -> +% {Key,atom_to_binary(Value,utf8)}; +% Value when is_pid(Value) == true -> +% {Key,list_to_binary(pid_to_list(Value))}; +% {M,F,A} -> {Key,list_to_binary(["{", atom_to_list(M), ":", atom_to_list(F), "/", integer_to_list(A), "}"])}; +% [H|E]-> +% {Key,[to_json(H)]++lists:map(fun(X) -> to_json(X) end,E)}; +% [] -> {Key,[]}; +% _ -> {Key,Value} +% end; +% T when is_atom(T) == true -> + % atom_to_binary(T,utf8); + % T when is_pid(T) == true -> + % list_to_binary(pid_to_list(T)); + % T when is_port(T) == true -> + % list_to_binary(erlang:port_to_list(T)) + % end. From 9c990175fb0e72ba6b9c033532dc0bb4ef534f23 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 14 Nov 2013 17:47:46 +0800 Subject: [PATCH 223/405] let application work --- src/bigwig_http_appmon.erl | 2 -- src/bigwig_http_stats_stream.erl | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bigwig_http_appmon.erl b/src/bigwig_http_appmon.erl index 58b2ccb..08fb742 100644 --- a/src/bigwig_http_appmon.erl +++ b/src/bigwig_http_appmon.erl @@ -25,9 +25,7 @@ handle_path(<<"GET">>, [<<"appmon">>], Req, State) -> [{name, App}, {desc, ?L2B(Desc)}, {ver, ?L2B(Ver)}]} || {Pid, _, {App, Desc, Ver}} <- RawApps], - io:format("Info is ~p", [Info]), Body = jsx:term_to_json(Info), - io:format("Body is ~p", [Body]), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}; diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index cb2a962..217dba6 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -28,7 +28,7 @@ websocket_info({bigwig, {bigwig_stats_sender, Stats}}, Req, State) -> {reply, {text, Reply}, Req, State}; websocket_info({bigwig, {bigwig_appmon, Stats}}, Req, State) -> - Reply = jsx:term_to_json([{appmon, [Stats]}]), + Reply = jsx:term_to_json([{appmon, Stats}]), {reply, {text, Reply}, Req, State}; websocket_info({bigwig, _}, Req, State) -> From e022ebb81fbbebc18aaf48fa2c56f83177c1c944 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 14 Nov 2013 17:47:46 +0800 Subject: [PATCH 224/405] let application work --- src/bigwig_http_appmon.erl | 2 -- src/bigwig_http_stats_stream.erl | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bigwig_http_appmon.erl b/src/bigwig_http_appmon.erl index 58b2ccb..08fb742 100644 --- a/src/bigwig_http_appmon.erl +++ b/src/bigwig_http_appmon.erl @@ -25,9 +25,7 @@ handle_path(<<"GET">>, [<<"appmon">>], Req, State) -> [{name, App}, {desc, ?L2B(Desc)}, {ver, ?L2B(Ver)}]} || {Pid, _, {App, Desc, Ver}} <- RawApps], - io:format("Info is ~p", [Info]), Body = jsx:term_to_json(Info), - io:format("Body is ~p", [Body]), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}; diff --git a/src/bigwig_http_stats_stream.erl b/src/bigwig_http_stats_stream.erl index cb2a962..217dba6 100644 --- a/src/bigwig_http_stats_stream.erl +++ b/src/bigwig_http_stats_stream.erl @@ -28,7 +28,7 @@ websocket_info({bigwig, {bigwig_stats_sender, Stats}}, Req, State) -> {reply, {text, Reply}, Req, State}; websocket_info({bigwig, {bigwig_appmon, Stats}}, Req, State) -> - Reply = jsx:term_to_json([{appmon, [Stats]}]), + Reply = jsx:term_to_json([{appmon, Stats}]), {reply, {text, Reply}, Req, State}; websocket_info({bigwig, _}, Req, State) -> From da174ef1b633bd0cd2b2d733b921e8a7b664e0c5 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 14 Nov 2013 18:03:44 +0800 Subject: [PATCH 225/405] jsx --- rebar.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 75f5ea3..07efb37 100644 --- a/rebar.config +++ b/rebar.config @@ -7,5 +7,6 @@ {deps, [ {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, - {jsx, ".*", {git, "git://github.com/yuzhaoren/jsx.git", "master"}} + {jsx, ".*", {git, "https://github.com/yuzhaoren/jsx.git", {branch, "beamspirit"}}} + %%{jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} ]}. From 4792a33d7902b0b6575d72098847b5e8ee90e71f Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 15 Nov 2013 14:52:11 +0800 Subject: [PATCH 226/405] let systeminfo show --- priv/html/index.html | 10 +++++----- priv/js/et.js | 3 ++- src/etop2.erl | 8 +++++++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index 8db17d9..97f622f 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -71,15 +71,15 @@

    BigWig

    System Info
    Process Limit
    -
    +
    Kernel Poll
    -
    +
    Logical Processors
    -
    +
    OTP Release
    -
    +
    Architecture
    -
    +

    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 diff --git a/src/bigwig.app.src b/src/bigwig.app.src index 41db7ac..1d701bf 100644 --- a/src/bigwig.app.src +++ b/src/bigwig.app.src @@ -6,6 +6,8 @@ {applications, [ kernel, stdlib, + crypto, + ranch, cowboy, jsx ]}, diff --git a/src/bigwig.erl b/src/bigwig.erl index 7280c79..d92b01e 100644 --- a/src/bigwig.erl +++ b/src/bigwig.erl @@ -1,18 +1,14 @@ -module(bigwig). -export([start/0, stop/0]). -ensure_started(App) -> - case application:start(App) of - ok -> - ok; - {error, {already_started, App}} -> - ok - end. - start() -> - ensure_started(crypto), - ensure_started(sasl), - ensure_started(cowboy), + application:ensure_started(ranch), + application:ensure_started(jsx), + + application:ensure_started(crypto), + application:ensure_started(sasl), + application:ensure_started(cowlib), + application:ensure_started(cowboy), application:start(bigwig). stop() -> diff --git a/src/bigwig_http.erl b/src/bigwig_http.erl index f0b1bdc..b9de8db 100644 --- a/src/bigwig_http.erl +++ b/src/bigwig_http.erl @@ -4,7 +4,12 @@ -export([start_link/0]). --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). -record(state, {}). @@ -51,7 +56,7 @@ init([]) -> error_logger:info_msg("Bigwig listening on http://~s:~B/~n", [IpStr,Port]), %% %% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts - cowboy:start_listener(http, NumAcceptors, + ranch:start_listener(http, NumAcceptors, cowboy_tcp_transport, [{port, Port}], cowboy_http_protocol, [{dispatch, dispatch_rules()}] ), diff --git a/src/bigwig_http_appmon.erl b/src/bigwig_http_appmon.erl index aca9cc6..d907c85 100644 --- a/src/bigwig_http_appmon.erl +++ b/src/bigwig_http_appmon.erl @@ -3,7 +3,7 @@ %% -module(bigwig_http_appmon). -behaviour(cowboy_http_handler). --export([init/3, handle/2, terminate/2]). +-export([init/3, handle/2, terminate/3]). -define(P2B(P), list_to_binary(pid_to_list(P))). -define(L2B(L), list_to_binary(L)). @@ -46,5 +46,5 @@ not_found(Req, State) -> {ok, Req2} = cowboy_http_req:reply(404, [], <<"

    diff --git a/priv/js/et.js b/priv/js/et.js index aa9ebba..826a0a9 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -30,7 +30,8 @@ $(document).ready(function() { json.aaData[i][6] = (Mfa)[0].outerHTML; } - var gs = ['node','clock','cpu','tot','bin','nprocs','procs','code','runqueue','atom','ets']; + var gs = ['node','clock','cpu','tot','bin','nprocs','procs','code','runqueue','atom','ets', + 'limit','poll','processors','release','architecture']; for (i = 0; i < gs.length; i++) { var g = gs[i]; if (g in header) { diff --git a/src/etop2.erl b/src/etop2.erl index 8ac5ba9..17996ef 100644 --- a/src/etop2.erl +++ b/src/etop2.erl @@ -309,7 +309,13 @@ update_json(Info, #opts{node=Node, accum=Accum}) -> {<<"code">>, Code}, {<<"runqueue">>, RQ}, {<<"atom">>, Atom}, - {<<"ets">>, Ets}] + {<<"ets">>, Ets}, + {<<"limit">>, erlang:system_info(process_limit)}, + {<<"poll">>,erlang:system_info(kernel_poll)}, + {<<"processors">>,erlang:system_info(logical_processors)}, + {<<"release">>,erlang:system_info(otp_release)}, + {<<"architecture">>,erlang:system_info(system_architecture)} + ] end, Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], %io:format("ps is ~p",[Ps]), From e2dfd1b03bea6fd36b19bca55878f4228b9fd6a8 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 15 Nov 2013 14:52:11 +0800 Subject: [PATCH 227/405] let systeminfo show --- priv/html/index.html | 10 +++++----- priv/js/et.js | 3 ++- src/etop2.erl | 8 +++++++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index 8db17d9..97f622f 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -71,15 +71,15 @@

    BigWig

    System Info
    Process Limit
    -
    +
    Kernel Poll
    -
    +
    Logical Processors
    -
    +
    OTP Release
    -
    +
    Architecture
    -
    +
    diff --git a/priv/js/et.js b/priv/js/et.js index aa9ebba..826a0a9 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -30,7 +30,8 @@ $(document).ready(function() { json.aaData[i][6] = (Mfa)[0].outerHTML; } - var gs = ['node','clock','cpu','tot','bin','nprocs','procs','code','runqueue','atom','ets']; + var gs = ['node','clock','cpu','tot','bin','nprocs','procs','code','runqueue','atom','ets', + 'limit','poll','processors','release','architecture']; for (i = 0; i < gs.length; i++) { var g = gs[i]; if (g in header) { diff --git a/src/etop2.erl b/src/etop2.erl index 8ac5ba9..17996ef 100644 --- a/src/etop2.erl +++ b/src/etop2.erl @@ -309,7 +309,13 @@ update_json(Info, #opts{node=Node, accum=Accum}) -> {<<"code">>, Code}, {<<"runqueue">>, RQ}, {<<"atom">>, Atom}, - {<<"ets">>, Ets}] + {<<"ets">>, Ets}, + {<<"limit">>, erlang:system_info(process_limit)}, + {<<"poll">>,erlang:system_info(kernel_poll)}, + {<<"processors">>,erlang:system_info(logical_processors)}, + {<<"release">>,erlang:system_info(otp_release)}, + {<<"architecture">>,erlang:system_info(system_architecture)} + ] end, Ps = [etop_proc_info_to_json(P) || P <- Info#etop_info.procinfo], %io:format("ps is ~p",[Ps]), From eed7263466bd7e695610395d215d22fab5743ed9 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 15 Nov 2013 15:43:38 +0800 Subject: [PATCH 228/405] let systeminfo show --- src/etop2.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/etop2.erl b/src/etop2.erl index 17996ef..77023b2 100644 --- a/src/etop2.erl +++ b/src/etop2.erl @@ -299,6 +299,13 @@ update_json(Info, #opts{node=Node, accum=Accum}) -> Memi -> [Tot,Procs,Atom,Bin,Code,Ets] = etop2:meminfo(Memi, [total,processes,atom,binary,code,ets]), + case erlang:system_info(kernel_poll) of + false -> + Poll = 0; + true -> + Poll = 1 + end, + [{<<"node">>, atom_to_binary(Node, utf8)}, {<<"clock">>, Clock}, {<<"cpu">>, Cpu}, @@ -311,7 +318,7 @@ update_json(Info, #opts{node=Node, accum=Accum}) -> {<<"atom">>, Atom}, {<<"ets">>, Ets}, {<<"limit">>, erlang:system_info(process_limit)}, - {<<"poll">>,erlang:system_info(kernel_poll)}, + {<<"poll">>, Poll},%erlang:system_info(kernel_poll)}, {<<"processors">>,erlang:system_info(logical_processors)}, {<<"release">>,erlang:system_info(otp_release)}, {<<"architecture">>,erlang:system_info(system_architecture)} From 90dd015ede518b1c749d95245831fade21ca7711 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 15 Nov 2013 15:43:38 +0800 Subject: [PATCH 229/405] let systeminfo show --- src/etop2.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/etop2.erl b/src/etop2.erl index 17996ef..77023b2 100644 --- a/src/etop2.erl +++ b/src/etop2.erl @@ -299,6 +299,13 @@ update_json(Info, #opts{node=Node, accum=Accum}) -> Memi -> [Tot,Procs,Atom,Bin,Code,Ets] = etop2:meminfo(Memi, [total,processes,atom,binary,code,ets]), + case erlang:system_info(kernel_poll) of + false -> + Poll = 0; + true -> + Poll = 1 + end, + [{<<"node">>, atom_to_binary(Node, utf8)}, {<<"clock">>, Clock}, {<<"cpu">>, Cpu}, @@ -311,7 +318,7 @@ update_json(Info, #opts{node=Node, accum=Accum}) -> {<<"atom">>, Atom}, {<<"ets">>, Ets}, {<<"limit">>, erlang:system_info(process_limit)}, - {<<"poll">>,erlang:system_info(kernel_poll)}, + {<<"poll">>, Poll},%erlang:system_info(kernel_poll)}, {<<"processors">>,erlang:system_info(logical_processors)}, {<<"release">>,erlang:system_info(otp_release)}, {<<"architecture">>,erlang:system_info(system_architecture)} From bd9b73e24722ab3dcae6a1a15855f900dac66916 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Mon, 18 Nov 2013 01:23:00 +0800 Subject: [PATCH 230/405] add module to fix some term_to_json issue in bigwig --- src/jsx_ext.erl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/jsx_ext.erl diff --git a/src/jsx_ext.erl b/src/jsx_ext.erl new file mode 100644 index 0000000..2d875a2 --- /dev/null +++ b/src/jsx_ext.erl @@ -0,0 +1,26 @@ +%%%------------------------------------------------------------------- +%%% @author Jack Tang +%%% @copyright (C) 2013, Jack Tang +%%% @doc +%%% +%%% @end +%%% Created : 18 Nov 2013 by Jack Tang +%%%------------------------------------------------------------------- +-module(jsx_ext). + +%% API +-export([]). + +%%%=================================================================== +%%% API +%%%=================================================================== + +%%-------------------------------------------------------------------- +%% @doc +%% @spec +%% @end +%%-------------------------------------------------------------------- + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== From 349e5cb0d0f4cd50f9b847ba18e9607af32e2c95 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Mon, 18 Nov 2013 01:23:00 +0800 Subject: [PATCH 231/405] add module to fix some term_to_json issue in bigwig --- src/jsx_ext.erl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/jsx_ext.erl diff --git a/src/jsx_ext.erl b/src/jsx_ext.erl new file mode 100644 index 0000000..2d875a2 --- /dev/null +++ b/src/jsx_ext.erl @@ -0,0 +1,26 @@ +%%%------------------------------------------------------------------- +%%% @author Jack Tang +%%% @copyright (C) 2013, Jack Tang +%%% @doc +%%% +%%% @end +%%% Created : 18 Nov 2013 by Jack Tang +%%%------------------------------------------------------------------- +-module(jsx_ext). + +%% API +-export([]). + +%%%=================================================================== +%%% API +%%%=================================================================== + +%%-------------------------------------------------------------------- +%% @doc +%% @spec +%% @end +%%-------------------------------------------------------------------- + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== From 663f3c6c91799995026b18887cb605a61c83ba37 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 18 Nov 2013 18:18:09 +0800 Subject: [PATCH 232/405] #5107 --- priv/html/index.html | 20 ++++++++++++++++++++ src/bigwig_app.erl | 1 + src/bigwig_http_lager.erl | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/bigwig_http_lager.erl diff --git a/priv/html/index.html b/priv/html/index.html index 97f622f..c5af825 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -17,6 +17,7 @@

    BigWig

  • Reports
  • Processes
  • Applications
  • +
  • Lager Logging
  • @@ -133,6 +134,25 @@

    BigWig

    +
    +
    +
    + Tracer: + +
    +
    +
    + + + + + + + + + +
    tracelog
    +
    @@ -133,6 +134,25 @@

    BigWig

    +
    +
    +
    + Tracer: + +
    +
    +
    + + + + + + + + + +
    tracelog
    +
    @@ -134,7 +134,7 @@

    BigWig

    -
    +
    Tracer: @@ -146,7 +146,6 @@

    BigWig

    trace - log diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index f6934f5..fa72e98 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -32,4 +32,14 @@ $(function() { $('#dashboard').append($('

    Dashboard dump

    ')); $('#dashboard').append($('
    ' + JSON.stringify(json, null, 4) + '
    ')); }); -}); + $.getJSON('/lager/status', function(json) { + $('#lager_status').append($('

    Lager Status

    ')); + var jsonText = JSON.stringify(json, null, 4); +// for(var i=0; i' + jsonText.ActiveTraces[i] + ''))[0].outerHTML; +// alert(jsonText.ActiveTraces[i]); +// } + $('#lager_status').append($('
    ' + jsonText + '
    ')); + }); +}); \ No newline at end of file diff --git a/src/bigwig.erl b/src/bigwig.erl index d92b01e..6fdee28 100644 --- a/src/bigwig.erl +++ b/src/bigwig.erl @@ -9,6 +9,7 @@ start() -> application:ensure_started(sasl), application:ensure_started(cowlib), application:ensure_started(cowboy), + application:ensure_started(lager), application:start(bigwig). stop() -> diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 0820069..7e76d9c 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -21,11 +21,11 @@ start(_StartType, _StartArgs) -> {"/rb/[...]", bigwig_http_rb, []}, {"/rb/stream", bigwig_http_rb_stream, []}, {"/pid/[...]", bigwig_http_pid, []}, - {"/top", bigwig_http_etop2, []}, + {"/top/[...]", bigwig_http_etop2, []}, {"/appmon/[...]", bigwig_http_appmon, []}, {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, - {"/log/[...]", bigwig_http_lager, []}, + {"/lager/[...]", bigwig_http_lager, []}, {'_', bigwig_http_catchall, []} ]} ]), diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 9224ec6..a171671 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -11,11 +11,12 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), + io:format("Path is ~p,Method is ~p",[Path,Method]), Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), handle_path(Method, Path1, Req1, State). handle_path(<<"GET">>, [<<"lager">>, <<"status">>], Req, State) -> - not_found(Req, State); + handle_get_status(Req, State); handle_path(<<"GET">>, [<<"lager">>, <<"tracer">>, RoutingKey], Req, State) -> not_found(Req, State); handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> @@ -25,9 +26,29 @@ handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> handle_path(_, _, Req, State) -> not_found(Req, State). +handle_get_status(Req,State) -> + Status = lager:status(), + Status1 = remove(Status), + Body = jsx:term_to_json(Status1), + Headers = [{<<"Content-Type">>, <<"application/json">>}], + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. + not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> ok. + +remove(Status) -> + [_,LStatus,_,Traces,Reductions,_] = Status, + LStatus1=lists:delete([],LStatus), + [_, Reductions1] = Reductions, + Reductions2 = lists:delete("\n",Reductions1), + Status1 = [ + {<<"Lager status">>, LStatus1}, + {<<"Active Traces">>, Traces}, + {<<"Tracing Reductions">>,Reductions2} + ], + Status1. \ No newline at end of file From 729080ea272ceeb21dc8f524e17f5d5167e82fd1 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 19 Nov 2013 18:21:34 +0800 Subject: [PATCH 235/405] #5107 let lager:status show --- priv/html/index.html | 5 ++--- priv/js/bigwig.js | 12 +++++++++++- src/bigwig.erl | 1 + src/bigwig_app.erl | 4 ++-- src/bigwig_http_lager.erl | 23 ++++++++++++++++++++++- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index c5af825..ce72e27 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -17,7 +17,7 @@

    BigWig

  • Reports
  • Processes
  • Applications
  • -
  • Lager Logging
  • +
  • Lager Logging
  • @@ -134,7 +134,7 @@

    BigWig

    -
    +
    Tracer: @@ -146,7 +146,6 @@

    BigWig

    trace - log diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index f6934f5..fa72e98 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -32,4 +32,14 @@ $(function() { $('#dashboard').append($('

    Dashboard dump

    ')); $('#dashboard').append($('
    ' + JSON.stringify(json, null, 4) + '
    ')); }); -}); + $.getJSON('/lager/status', function(json) { + $('#lager_status').append($('

    Lager Status

    ')); + var jsonText = JSON.stringify(json, null, 4); +// for(var i=0; i' + jsonText.ActiveTraces[i] + ''))[0].outerHTML; +// alert(jsonText.ActiveTraces[i]); +// } + $('#lager_status').append($('
    ' + jsonText + '
    ')); + }); +}); \ No newline at end of file diff --git a/src/bigwig.erl b/src/bigwig.erl index d92b01e..6fdee28 100644 --- a/src/bigwig.erl +++ b/src/bigwig.erl @@ -9,6 +9,7 @@ start() -> application:ensure_started(sasl), application:ensure_started(cowlib), application:ensure_started(cowboy), + application:ensure_started(lager), application:start(bigwig). stop() -> diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 0820069..7e76d9c 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -21,11 +21,11 @@ start(_StartType, _StartArgs) -> {"/rb/[...]", bigwig_http_rb, []}, {"/rb/stream", bigwig_http_rb_stream, []}, {"/pid/[...]", bigwig_http_pid, []}, - {"/top", bigwig_http_etop2, []}, + {"/top/[...]", bigwig_http_etop2, []}, {"/appmon/[...]", bigwig_http_appmon, []}, {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, - {"/log/[...]", bigwig_http_lager, []}, + {"/lager/[...]", bigwig_http_lager, []}, {'_', bigwig_http_catchall, []} ]} ]), diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 9224ec6..a171671 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -11,11 +11,12 @@ init({tcp, http}, Req, _Opts) -> handle(Req0, State) -> {Path, Req} = cowboy_req:path(Req0), {Method, Req1} = cowboy_req:method(Req), + io:format("Path is ~p,Method is ~p",[Path,Method]), Path1=lists:delete(<<>>,binary:split(Path,[<<"/">>],[global])), handle_path(Method, Path1, Req1, State). handle_path(<<"GET">>, [<<"lager">>, <<"status">>], Req, State) -> - not_found(Req, State); + handle_get_status(Req, State); handle_path(<<"GET">>, [<<"lager">>, <<"tracer">>, RoutingKey], Req, State) -> not_found(Req, State); handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> @@ -25,9 +26,29 @@ handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> handle_path(_, _, Req, State) -> not_found(Req, State). +handle_get_status(Req,State) -> + Status = lager:status(), + Status1 = remove(Status), + Body = jsx:term_to_json(Status1), + Headers = [{<<"Content-Type">>, <<"application/json">>}], + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. + not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. terminate(_Reason, _Req, _State) -> ok. + +remove(Status) -> + [_,LStatus,_,Traces,Reductions,_] = Status, + LStatus1=lists:delete([],LStatus), + [_, Reductions1] = Reductions, + Reductions2 = lists:delete("\n",Reductions1), + Status1 = [ + {<<"Lager status">>, LStatus1}, + {<<"Active Traces">>, Traces}, + {<<"Tracing Reductions">>,Reductions2} + ], + Status1. \ No newline at end of file From 5e7f406220a4bb007317d6df465ee8db046771f1 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 19 Nov 2013 18:37:18 +0800 Subject: [PATCH 236/405] #5107 --- src/bigwig_http_lager.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index a171671..03efcbe 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -18,7 +18,7 @@ handle(Req0, State) -> handle_path(<<"GET">>, [<<"lager">>, <<"status">>], Req, State) -> handle_get_status(Req, State); handle_path(<<"GET">>, [<<"lager">>, <<"tracer">>, RoutingKey], Req, State) -> - not_found(Req, State); + handle_get_log(RoutingKey, Req, State); handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> not_found(Req, State); handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> @@ -33,7 +33,8 @@ handle_get_status(Req,State) -> Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. - +handle_get_log(RoutingKey, Req, State) -> + {ok, Req, State}. not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. From 64d3de7a5113945dc5e71e2b03ed13961c46db49 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 19 Nov 2013 18:37:18 +0800 Subject: [PATCH 237/405] #5107 --- src/bigwig_http_lager.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index a171671..03efcbe 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -18,7 +18,7 @@ handle(Req0, State) -> handle_path(<<"GET">>, [<<"lager">>, <<"status">>], Req, State) -> handle_get_status(Req, State); handle_path(<<"GET">>, [<<"lager">>, <<"tracer">>, RoutingKey], Req, State) -> - not_found(Req, State); + handle_get_log(RoutingKey, Req, State); handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> not_found(Req, State); handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> @@ -33,7 +33,8 @@ handle_get_status(Req,State) -> Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. - +handle_get_log(RoutingKey, Req, State) -> + {ok, Req, State}. not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. From a63a935ef87978fea83c379d627e7f7292c89591 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 20 Nov 2013 18:52:19 +0800 Subject: [PATCH 238/405] 5107 --- priv/js/bigwig.js | 15 ++++++++------- priv/js/et.js | 18 ++++++++++++++++++ priv/js/renderer.js | 26 ++++++++++++++++++++++++-- priv/js/tpl.js | 1 - rebar.config | 3 ++- src/bigwig_http_lager.erl | 8 ++++++-- 6 files changed, 58 insertions(+), 13 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index fa72e98..bfdfe4a 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -34,12 +34,13 @@ $(function() { }); $.getJSON('/lager/status', function(json) { $('#lager_status').append($('

    Lager Status

    ')); - var jsonText = JSON.stringify(json, null, 4); -// for(var i=0; i' + jsonText.ActiveTraces[i] + ''))[0].outerHTML; -// alert(jsonText.ActiveTraces[i]); -// } - $('#lager_status').append($('
    ' + jsonText + '
    ')); + for(var i=0; i' + json.ActiveTraces[i] + ''))[0].outerHTML; + } + var jsonText=JSON.stringify(json, null, 4); + var result=jsonText.replace(/\\"/g,"\""); + var result1=result.replace(/ /g,""); + $('#lager_status').append($('
    ' + result1 + '
    ')); }); }); \ No newline at end of file diff --git a/priv/js/et.js b/priv/js/et.js index 826a0a9..d281dca 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -42,6 +42,24 @@ $(document).ready(function() { }); } }); + var oTable1 = $('#log_table').dataTable({ + "bProcessing": false, + "iDisplayLength": 25, + "sAjaxSource": '/lager/status', + "aaSorting": [[5, "desc"]], + // Get data piggy-backed on the main table and update the page + "fnServerData": function(sSource, aaData, fnCallback) { + $.getJSON( sSource, aaData, function(json) { + + // Use our RENDERER to format everything + for(var i=0; i' + json.ActiveTraces[i] + ''))[0].outerHTML; + } + fnCallback(json); + }); + } + }); $(document).everyTime(5000, "poll_etop", function() {oTable.fnReloadAjax();}); $('#accumulate').bind('click', function() { $.ajax({ diff --git a/priv/js/renderer.js b/priv/js/renderer.js index ed730b9..cd41825 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -123,7 +123,25 @@ var RENDERER = (function() { }) }}); } - + var show_log_dialog = function(r) { + var url = '/lager/tracer' + r; + alert(url); + $.ajax({ + url: url, + dataType: 'json', + success: function(json){ + $('
    ') + .dialog({ + title: r + ':trace_info()   ' + + '(json)', + width: 550, + buttons: { + "Close": function() { $(this).dialog("close"); } + } + }) + } + }); + } var gen_pid_html = function( json ) { var t = $('
    '); @@ -264,4 +282,8 @@ $("body").delegate("a.mfa", "click", function(e){ RENDERER.show_mfa_dialog(x1[0],x2[0],parseInt(x2[1])); e.preventDefault(); }); - +$("body").delegate("a.tracer", "click", function(e){ + var tracer = 123; + RENDERER.show_log_dialog(tracer); + e.preventDefault(); +}); diff --git a/priv/js/tpl.js b/priv/js/tpl.js index 699946d..fab8d29 100644 --- a/priv/js/tpl.js +++ b/priv/js/tpl.js @@ -147,7 +147,6 @@ var TPL = (function() { } } var jt = type(data); - if(jt == "string") { try { el.html(data); diff --git a/rebar.config b/rebar.config index 07efb37..69b67c0 100644 --- a/rebar.config +++ b/rebar.config @@ -7,6 +7,7 @@ {deps, [ {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, - {jsx, ".*", {git, "https://github.com/yuzhaoren/jsx.git", {branch, "beamspirit"}}} + {jsx, ".*", {git, "https://github.com/yuzhaoren/jsx.git", {branch, "beamspirit"}}}, + {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}} %%{jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} ]}. diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 03efcbe..f5f87d6 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -34,7 +34,11 @@ handle_get_status(Req,State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_get_log(RoutingKey, Req, State) -> - {ok, Req, State}. +% amqp_subscriber:start_link(RoutingKey), + Body = jsx:term_to_json(RoutingKey), + Headers = [{<<"Content-Type">>, <<"application/json">>}], + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. @@ -49,7 +53,7 @@ remove(Status) -> Reductions2 = lists:delete("\n",Reductions1), Status1 = [ {<<"Lager status">>, LStatus1}, - {<<"Active Traces">>, Traces}, + {<<"ActiveTraces">>, Traces}, {<<"Tracing Reductions">>,Reductions2} ], Status1. \ No newline at end of file From c1c9ebc14cf1e70ea63e52aba11cb3783e6f2753 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 20 Nov 2013 18:52:19 +0800 Subject: [PATCH 239/405] 5107 --- priv/js/bigwig.js | 15 ++++++++------- priv/js/et.js | 18 ++++++++++++++++++ priv/js/renderer.js | 26 ++++++++++++++++++++++++-- priv/js/tpl.js | 1 - src/bigwig_http_lager.erl | 8 ++++++-- 5 files changed, 56 insertions(+), 12 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index fa72e98..bfdfe4a 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -34,12 +34,13 @@ $(function() { }); $.getJSON('/lager/status', function(json) { $('#lager_status').append($('

    Lager Status

    ')); - var jsonText = JSON.stringify(json, null, 4); -// for(var i=0; i' + jsonText.ActiveTraces[i] + ''))[0].outerHTML; -// alert(jsonText.ActiveTraces[i]); -// } - $('#lager_status').append($('
    ' + jsonText + '
    ')); + for(var i=0; i' + json.ActiveTraces[i] + ''))[0].outerHTML; + } + var jsonText=JSON.stringify(json, null, 4); + var result=jsonText.replace(/\\"/g,"\""); + var result1=result.replace(/ /g,""); + $('#lager_status').append($('
    ' + result1 + '
    ')); }); }); \ No newline at end of file diff --git a/priv/js/et.js b/priv/js/et.js index 826a0a9..d281dca 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -42,6 +42,24 @@ $(document).ready(function() { }); } }); + var oTable1 = $('#log_table').dataTable({ + "bProcessing": false, + "iDisplayLength": 25, + "sAjaxSource": '/lager/status', + "aaSorting": [[5, "desc"]], + // Get data piggy-backed on the main table and update the page + "fnServerData": function(sSource, aaData, fnCallback) { + $.getJSON( sSource, aaData, function(json) { + + // Use our RENDERER to format everything + for(var i=0; i' + json.ActiveTraces[i] + ''))[0].outerHTML; + } + fnCallback(json); + }); + } + }); $(document).everyTime(5000, "poll_etop", function() {oTable.fnReloadAjax();}); $('#accumulate').bind('click', function() { $.ajax({ diff --git a/priv/js/renderer.js b/priv/js/renderer.js index ed730b9..cd41825 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -123,7 +123,25 @@ var RENDERER = (function() { }) }}); } - + var show_log_dialog = function(r) { + var url = '/lager/tracer' + r; + alert(url); + $.ajax({ + url: url, + dataType: 'json', + success: function(json){ + $('
    ') + .dialog({ + title: r + ':trace_info()   ' + + '(json)', + width: 550, + buttons: { + "Close": function() { $(this).dialog("close"); } + } + }) + } + }); + } var gen_pid_html = function( json ) { var t = $('
    '); @@ -264,4 +282,8 @@ $("body").delegate("a.mfa", "click", function(e){ RENDERER.show_mfa_dialog(x1[0],x2[0],parseInt(x2[1])); e.preventDefault(); }); - +$("body").delegate("a.tracer", "click", function(e){ + var tracer = 123; + RENDERER.show_log_dialog(tracer); + e.preventDefault(); +}); diff --git a/priv/js/tpl.js b/priv/js/tpl.js index 699946d..fab8d29 100644 --- a/priv/js/tpl.js +++ b/priv/js/tpl.js @@ -147,7 +147,6 @@ var TPL = (function() { } } var jt = type(data); - if(jt == "string") { try { el.html(data); diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 03efcbe..f5f87d6 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -34,7 +34,11 @@ handle_get_status(Req,State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_get_log(RoutingKey, Req, State) -> - {ok, Req, State}. +% amqp_subscriber:start_link(RoutingKey), + Body = jsx:term_to_json(RoutingKey), + Headers = [{<<"Content-Type">>, <<"application/json">>}], + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. @@ -49,7 +53,7 @@ remove(Status) -> Reductions2 = lists:delete("\n",Reductions1), Status1 = [ {<<"Lager status">>, LStatus1}, - {<<"Active Traces">>, Traces}, + {<<"ActiveTraces">>, Traces}, {<<"Tracing Reductions">>,Reductions2} ], Status1. \ No newline at end of file From 7c7095816a68b1ac8b54fb39360e0075f04184ed Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 21 Nov 2013 15:38:34 +0800 Subject: [PATCH 240/405] #5107 --- src/bigwig_app.erl | 3 ++- src/bigwig_http_lager.erl | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 7e76d9c..d8c65a6 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -18,13 +18,14 @@ start(_StartType, _StartArgs) -> {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", [{mimetypes, cow_mimetypes, all}]}}, {"/vm", bigwig_http_vm, []}, + {"/rb/stream", bigwig_http_rb_stream, []}, {"/rb/[...]", bigwig_http_rb, []}, - {"/rb/stream", bigwig_http_rb_stream, []}, {"/pid/[...]", bigwig_http_pid, []}, {"/top/[...]", bigwig_http_etop2, []}, {"/appmon/[...]", bigwig_http_appmon, []}, {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, + {"/lager/stream", bigwig_http_lager_stream, []}, {"/lager/[...]", bigwig_http_lager, []}, {'_', bigwig_http_catchall, []} ]} diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index f5f87d6..25b0213 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -34,8 +34,10 @@ handle_get_status(Req,State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_get_log(RoutingKey, Req, State) -> -% amqp_subscriber:start_link(RoutingKey), - Body = jsx:term_to_json(RoutingKey), + amqp_subscriber:start_link(<<"trace.*">>), + {ok,Info}=file:read_file("trace.log"), + Msg={bigwig_trace, Info}, + Body = jsx:term_to_json(Msg), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. From a9f44c55e11c3280d540ef39bb8ac62d574350dd Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 21 Nov 2013 15:38:34 +0800 Subject: [PATCH 241/405] #5107 --- src/bigwig_app.erl | 3 ++- src/bigwig_http_lager.erl | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 7e76d9c..d8c65a6 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -18,13 +18,14 @@ start(_StartType, _StartArgs) -> {"/static/[...]", cowboy_static, {priv_dir, bigwig, "", [{mimetypes, cow_mimetypes, all}]}}, {"/vm", bigwig_http_vm, []}, + {"/rb/stream", bigwig_http_rb_stream, []}, {"/rb/[...]", bigwig_http_rb, []}, - {"/rb/stream", bigwig_http_rb_stream, []}, {"/pid/[...]", bigwig_http_pid, []}, {"/top/[...]", bigwig_http_etop2, []}, {"/appmon/[...]", bigwig_http_appmon, []}, {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, + {"/lager/stream", bigwig_http_lager_stream, []}, {"/lager/[...]", bigwig_http_lager, []}, {'_', bigwig_http_catchall, []} ]} diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index f5f87d6..25b0213 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -34,8 +34,10 @@ handle_get_status(Req,State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_get_log(RoutingKey, Req, State) -> -% amqp_subscriber:start_link(RoutingKey), - Body = jsx:term_to_json(RoutingKey), + amqp_subscriber:start_link(<<"trace.*">>), + {ok,Info}=file:read_file("trace.log"), + Msg={bigwig_trace, Info}, + Body = jsx:term_to_json(Msg), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. From 23b13654c6c025f0d815f1142fb395048eb3b5c8 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 10:57:28 +0800 Subject: [PATCH 242/405] #5107 --- priv/html/index.html | 4 ++-- priv/js/et.js | 18 ------------------ priv/js/renderer.js | 28 ++++------------------------ src/bigwig_http_lager.erl | 4 ++-- 4 files changed, 8 insertions(+), 46 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index ce72e27..b978163 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -145,7 +145,7 @@

    BigWig

    - + @@ -201,6 +201,6 @@

    BigWig

    - + diff --git a/priv/js/et.js b/priv/js/et.js index d281dca..826a0a9 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -42,24 +42,6 @@ $(document).ready(function() { }); } }); - var oTable1 = $('#log_table').dataTable({ - "bProcessing": false, - "iDisplayLength": 25, - "sAjaxSource": '/lager/status', - "aaSorting": [[5, "desc"]], - // Get data piggy-backed on the main table and update the page - "fnServerData": function(sSource, aaData, fnCallback) { - $.getJSON( sSource, aaData, function(json) { - - // Use our RENDERER to format everything - for(var i=0; i' + json.ActiveTraces[i] + ''))[0].outerHTML; - } - fnCallback(json); - }); - } - }); $(document).everyTime(5000, "poll_etop", function() {oTable.fnReloadAjax();}); $('#accumulate').bind('click', function() { $.ajax({ diff --git a/priv/js/renderer.js b/priv/js/renderer.js index cd41825..ce84fb6 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -123,25 +123,6 @@ var RENDERER = (function() { }) }}); } - var show_log_dialog = function(r) { - var url = '/lager/tracer' + r; - alert(url); - $.ajax({ - url: url, - dataType: 'json', - success: function(json){ - $('
    ') - .dialog({ - title: r + ':trace_info()   ' + - '(json)', - width: 550, - buttons: { - "Close": function() { $(this).dialog("close"); } - } - }) - } - }); - } var gen_pid_html = function( json ) { var t = $('
    tracetrace
    '); @@ -282,8 +263,7 @@ $("body").delegate("a.mfa", "click", function(e){ RENDERER.show_mfa_dialog(x1[0],x2[0],parseInt(x2[1])); e.preventDefault(); }); -$("body").delegate("a.tracer", "click", function(e){ - var tracer = 123; - RENDERER.show_log_dialog(tracer); - e.preventDefault(); -}); + +// RENDERER.show_log_dialog(tracer); +// e.preventDefault(); + diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 25b0213..009248e 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -34,9 +34,9 @@ handle_get_status(Req,State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_get_log(RoutingKey, Req, State) -> - amqp_subscriber:start_link(<<"trace.*">>), +% amqp_subscriber:start_link(<<"trace.*">>), {ok,Info}=file:read_file("trace.log"), - Msg={bigwig_trace, Info}, + Msg=[{bigwig_trace, Info}], Body = jsx:term_to_json(Msg), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), From 6d5c3b635e69f331af28b2477adb7aa945f6405b Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 10:57:28 +0800 Subject: [PATCH 243/405] #5107 --- priv/html/index.html | 4 ++-- priv/js/et.js | 18 ------------------ priv/js/renderer.js | 28 ++++------------------------ src/bigwig_http_lager.erl | 4 ++-- 4 files changed, 8 insertions(+), 46 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index ce72e27..b978163 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -145,7 +145,7 @@

    BigWig

    - + @@ -201,6 +201,6 @@

    BigWig

    - + diff --git a/priv/js/et.js b/priv/js/et.js index d281dca..826a0a9 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -42,24 +42,6 @@ $(document).ready(function() { }); } }); - var oTable1 = $('#log_table').dataTable({ - "bProcessing": false, - "iDisplayLength": 25, - "sAjaxSource": '/lager/status', - "aaSorting": [[5, "desc"]], - // Get data piggy-backed on the main table and update the page - "fnServerData": function(sSource, aaData, fnCallback) { - $.getJSON( sSource, aaData, function(json) { - - // Use our RENDERER to format everything - for(var i=0; i' + json.ActiveTraces[i] + ''))[0].outerHTML; - } - fnCallback(json); - }); - } - }); $(document).everyTime(5000, "poll_etop", function() {oTable.fnReloadAjax();}); $('#accumulate').bind('click', function() { $.ajax({ diff --git a/priv/js/renderer.js b/priv/js/renderer.js index cd41825..ce84fb6 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -123,25 +123,6 @@ var RENDERER = (function() { }) }}); } - var show_log_dialog = function(r) { - var url = '/lager/tracer' + r; - alert(url); - $.ajax({ - url: url, - dataType: 'json', - success: function(json){ - $('
    ') - .dialog({ - title: r + ':trace_info()   ' + - '(json)', - width: 550, - buttons: { - "Close": function() { $(this).dialog("close"); } - } - }) - } - }); - } var gen_pid_html = function( json ) { var t = $('
    tracetrace
    '); @@ -282,8 +263,7 @@ $("body").delegate("a.mfa", "click", function(e){ RENDERER.show_mfa_dialog(x1[0],x2[0],parseInt(x2[1])); e.preventDefault(); }); -$("body").delegate("a.tracer", "click", function(e){ - var tracer = 123; - RENDERER.show_log_dialog(tracer); - e.preventDefault(); -}); + +// RENDERER.show_log_dialog(tracer); +// e.preventDefault(); + diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 25b0213..009248e 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -34,9 +34,9 @@ handle_get_status(Req,State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_get_log(RoutingKey, Req, State) -> - amqp_subscriber:start_link(<<"trace.*">>), +% amqp_subscriber:start_link(<<"trace.*">>), {ok,Info}=file:read_file("trace.log"), - Msg={bigwig_trace, Info}, + Msg=[{bigwig_trace, Info}], Body = jsx:term_to_json(Msg), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), From c981589fe4ec783e8cdd911b26ccec8d599cbf85 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 10:59:17 +0800 Subject: [PATCH 244/405] #5107 websocket --- priv/js/lager.js | 29 ++++++++++++++++++++++++ src/bigwig_http_lager_stream.erl | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 priv/js/lager.js create mode 100644 src/bigwig_http_lager_stream.erl diff --git a/priv/js/lager.js b/priv/js/lager.js new file mode 100644 index 0000000..ee78e6b --- /dev/null +++ b/priv/js/lager.js @@ -0,0 +1,29 @@ +$(document).ready(function(){ + connect("/lager/stream"); +} +function connect(to) +{ + var host = document.location.host; + websocket = new WebSocket("ws://"+host+to); + websocket.onopen = function(evt) { onOpen(evt) }; + websocket.onclose = function(evt) { onClose(evt) }; + websocket.onmessage = function(evt) { onMessage(evt) }; +}; + +function onOpen(evt) { +}; +function sendTxt(txt) { + websocket.send(txt); +}; +function onClose(evt) { +}; + +function onMessage(evt) { + $('#trace_log').prepend('

    ' + txt + '

    '); +}; + +$("body").delegate("a.tracer", "click", function(e){ + var x1 = $(this).text(); + sendTxt(x1); + e.preventDefault(); +}); \ No newline at end of file diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl new file mode 100644 index 0000000..8c96acf --- /dev/null +++ b/src/bigwig_http_lager_stream.erl @@ -0,0 +1,38 @@ +%% Websocket connection that sends log of trace +%% +-module(bigwig_http_lager_stream). +-behaviour(cowboy_websocket_handler). + +-export([init/3, handle/2, terminate/3]). +-export([websocket_init/3, websocket_handle/3, + websocket_info/3, websocket_terminate/3]). + + +init({tcp, http}, _Req, _Opts) -> + {upgrade, protocol, cowboy_websocket}. + +handle(_Req, _State) -> + exit(websockets_only). + +terminate(_Reason, _Req, _State) -> + exit(websockets_only). + +websocket_init(_TransportName, Req, _Opts) -> + bigwig_pubsubhub:register_client(self()), + {ok, Req, undefined_state}. + +%% TODO handle stuff like {bigwig, {appmon, ... }} and send that too +websocket_handle({text, _Msg}, Req, State) -> + amqp_subscriber:start_link(<<"trace.*">>), + {ok, Req, State}. +websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> + Reply = jsx:term_to_json([{lager, Stats}]), + {reply, {text, Reply}, Req, State}; +websocket_info({bigwig, _}, Req, State) -> + {ok, Req, State}; +websocket_info(Info, Req, State) -> + io:format("Unhandled msg to ~p ~p\n", [?MODULE, Info]), + {ok, Req, State}. + +websocket_terminate(_Reason, _Req, _State) -> ok. + From 2cd445b77b2372df8681e81fc1dbc0eaf214f5ce Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 10:59:17 +0800 Subject: [PATCH 245/405] #5107 websocket --- priv/js/lager.js | 29 ++++++++++++++++++++++++ src/bigwig_http_lager_stream.erl | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 priv/js/lager.js create mode 100644 src/bigwig_http_lager_stream.erl diff --git a/priv/js/lager.js b/priv/js/lager.js new file mode 100644 index 0000000..ee78e6b --- /dev/null +++ b/priv/js/lager.js @@ -0,0 +1,29 @@ +$(document).ready(function(){ + connect("/lager/stream"); +} +function connect(to) +{ + var host = document.location.host; + websocket = new WebSocket("ws://"+host+to); + websocket.onopen = function(evt) { onOpen(evt) }; + websocket.onclose = function(evt) { onClose(evt) }; + websocket.onmessage = function(evt) { onMessage(evt) }; +}; + +function onOpen(evt) { +}; +function sendTxt(txt) { + websocket.send(txt); +}; +function onClose(evt) { +}; + +function onMessage(evt) { + $('#trace_log').prepend('

    ' + txt + '

    '); +}; + +$("body").delegate("a.tracer", "click", function(e){ + var x1 = $(this).text(); + sendTxt(x1); + e.preventDefault(); +}); \ No newline at end of file diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl new file mode 100644 index 0000000..8c96acf --- /dev/null +++ b/src/bigwig_http_lager_stream.erl @@ -0,0 +1,38 @@ +%% Websocket connection that sends log of trace +%% +-module(bigwig_http_lager_stream). +-behaviour(cowboy_websocket_handler). + +-export([init/3, handle/2, terminate/3]). +-export([websocket_init/3, websocket_handle/3, + websocket_info/3, websocket_terminate/3]). + + +init({tcp, http}, _Req, _Opts) -> + {upgrade, protocol, cowboy_websocket}. + +handle(_Req, _State) -> + exit(websockets_only). + +terminate(_Reason, _Req, _State) -> + exit(websockets_only). + +websocket_init(_TransportName, Req, _Opts) -> + bigwig_pubsubhub:register_client(self()), + {ok, Req, undefined_state}. + +%% TODO handle stuff like {bigwig, {appmon, ... }} and send that too +websocket_handle({text, _Msg}, Req, State) -> + amqp_subscriber:start_link(<<"trace.*">>), + {ok, Req, State}. +websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> + Reply = jsx:term_to_json([{lager, Stats}]), + {reply, {text, Reply}, Req, State}; +websocket_info({bigwig, _}, Req, State) -> + {ok, Req, State}; +websocket_info(Info, Req, State) -> + io:format("Unhandled msg to ~p ~p\n", [?MODULE, Info]), + {ok, Req, State}. + +websocket_terminate(_Reason, _Req, _State) -> ok. + From 1a35a74b55319108ee614c5b3ce9e503267747cf Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 16:33:04 +0800 Subject: [PATCH 246/405] #5107 trace --- priv/html/index.html | 2 -- priv/js/bigwig.js | 23 ++++++++++++++++++++++- priv/js/lager.js | 4 ++-- priv/js/renderer.js | 6 +++++- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index b978163..3817208 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -185,7 +185,6 @@

    BigWig

    - @@ -201,6 +200,5 @@

    BigWig

    - diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index bfdfe4a..b0847cc 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -43,4 +43,25 @@ $(function() { var result1=result.replace(/ /g,""); $('#lager_status').append($('
    ' + result1 + '
    ')); }); -}); \ No newline at end of file + connect("/lager/stream"); +}); +function connect(to) +{ + var host = document.location.host; + websocket = new WebSocket("ws://"+host+to); + websocket.onopen = function(evt) { onOpen(evt) }; + websocket.onclose = function(evt) { onClose(evt) }; + websocket.onmessage = function(evt) { onMessage(evt) }; +}; + +function onOpen(evt) { +}; +function sendTxt(txt) { + websocket.send(txt); +}; +function onClose(evt) { +}; + +function onMessage(evt) { + $('#trace_log').prepend('

    ' + evt.data + '

    '); +}; \ No newline at end of file diff --git a/priv/js/lager.js b/priv/js/lager.js index ee78e6b..85d1532 100644 --- a/priv/js/lager.js +++ b/priv/js/lager.js @@ -1,5 +1,5 @@ $(document).ready(function(){ - connect("/lager/stream"); + } function connect(to) { @@ -19,7 +19,7 @@ function onClose(evt) { }; function onMessage(evt) { - $('#trace_log').prepend('

    ' + txt + '

    '); + $('#trace_log').prepend('

    ' + evt.data + '

    '); }; $("body").delegate("a.tracer", "click", function(e){ diff --git a/priv/js/renderer.js b/priv/js/renderer.js index ce84fb6..cc76445 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -263,7 +263,11 @@ $("body").delegate("a.mfa", "click", function(e){ RENDERER.show_mfa_dialog(x1[0],x2[0],parseInt(x2[1])); e.preventDefault(); }); - +$("body").delegate("a.tracer", "click", function(e){ + var x1 = $(this).text(); + sendTxt(x1); + e.preventDefault(); +}); // RENDERER.show_log_dialog(tracer); // e.preventDefault(); From 8c5e9a9406fcccdc642b23cc38899d9aee84aa9c Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 16:33:04 +0800 Subject: [PATCH 247/405] #5107 trace --- priv/html/index.html | 2 -- priv/js/bigwig.js | 23 ++++++++++++++++++++++- priv/js/lager.js | 4 ++-- priv/js/renderer.js | 6 +++++- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index b978163..3817208 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -185,7 +185,6 @@

    BigWig

    - @@ -201,6 +200,5 @@

    BigWig

    - diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index bfdfe4a..b0847cc 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -43,4 +43,25 @@ $(function() { var result1=result.replace(/ /g,""); $('#lager_status').append($('
    ' + result1 + '
    ')); }); -}); \ No newline at end of file + connect("/lager/stream"); +}); +function connect(to) +{ + var host = document.location.host; + websocket = new WebSocket("ws://"+host+to); + websocket.onopen = function(evt) { onOpen(evt) }; + websocket.onclose = function(evt) { onClose(evt) }; + websocket.onmessage = function(evt) { onMessage(evt) }; +}; + +function onOpen(evt) { +}; +function sendTxt(txt) { + websocket.send(txt); +}; +function onClose(evt) { +}; + +function onMessage(evt) { + $('#trace_log').prepend('

    ' + evt.data + '

    '); +}; \ No newline at end of file diff --git a/priv/js/lager.js b/priv/js/lager.js index ee78e6b..85d1532 100644 --- a/priv/js/lager.js +++ b/priv/js/lager.js @@ -1,5 +1,5 @@ $(document).ready(function(){ - connect("/lager/stream"); + } function connect(to) { @@ -19,7 +19,7 @@ function onClose(evt) { }; function onMessage(evt) { - $('#trace_log').prepend('

    ' + txt + '

    '); + $('#trace_log').prepend('

    ' + evt.data + '

    '); }; $("body").delegate("a.tracer", "click", function(e){ diff --git a/priv/js/renderer.js b/priv/js/renderer.js index ce84fb6..cc76445 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -263,7 +263,11 @@ $("body").delegate("a.mfa", "click", function(e){ RENDERER.show_mfa_dialog(x1[0],x2[0],parseInt(x2[1])); e.preventDefault(); }); - +$("body").delegate("a.tracer", "click", function(e){ + var x1 = $(this).text(); + sendTxt(x1); + e.preventDefault(); +}); // RENDERER.show_log_dialog(tracer); // e.preventDefault(); From daaa3d684c3f7b1d991765be87bede7d79d6b023 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 16:35:15 +0800 Subject: [PATCH 248/405] #5107 --- src/bigwig_appmon_info.erl | 15 ++++++++++----- src/bigwig_http_lager_stream.erl | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/bigwig_appmon_info.erl b/src/bigwig_appmon_info.erl index ccb334a..a842fa6 100644 --- a/src/bigwig_appmon_info.erl +++ b/src/bigwig_appmon_info.erl @@ -370,19 +370,23 @@ find_avoid() -> [undefined], ?IGNORE_APPS). -pid_info({P}) -> +pid_info({P}) -> + Node = node(P), L = - case process_info(P, registered_name) of +% case process_info(P, registered_name) of + case rpc:call(Node,erlang, process_info, [P,registered_name]) of {registered_name, Name} -> [{name, Name}]; _ -> [] end, L1 = - case process_info(P, current_function) of +% case process_info(P, current_function) of + case rpc:call(Node,erlang, process_info, [P,current_function]) of {current_function, CMfa} -> [{cf, tuple_to_list(CMfa)}]; _ -> [] end, L2 = - case process_info(P, dictionary) of +% case process_info(P, dictionary) of + case rpc:call(Node,erlang, process_info, [P,dictionary]) of {dictionary, Dict} when is_list(Dict) -> case lists:keyfind('$initial_call', 1, Dict) of {'$initial_call', IcMfa} -> [{ic, tuple_to_list(IcMfa)}]; @@ -391,7 +395,8 @@ pid_info({P}) -> _ -> [] end, L3 = - case process_info(P, message_queue_len) of +% case process_info(P, message_queue_len) of + case rpc:call(Node,erlang, process_info, [P,message_queue_len]) of {message_queue_len, QLen} -> [{q, QLen}]; _ -> [] end, diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 8c96acf..5bd7717 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -18,6 +18,7 @@ terminate(_Reason, _Req, _State) -> exit(websockets_only). websocket_init(_TransportName, Req, _Opts) -> + io:format("lager_stream init ok~n"), bigwig_pubsubhub:register_client(self()), {ok, Req, undefined_state}. @@ -27,6 +28,7 @@ websocket_handle({text, _Msg}, Req, State) -> {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), + io:format("Reply is ~p",[Reply]), {reply, {text, Reply}, Req, State}; websocket_info({bigwig, _}, Req, State) -> {ok, Req, State}; From 49232ff8e51577979313f49847ffe4fbc56d7755 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 16:35:15 +0800 Subject: [PATCH 249/405] #5107 --- src/bigwig_appmon_info.erl | 15 ++++++++++----- src/bigwig_http_lager_stream.erl | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/bigwig_appmon_info.erl b/src/bigwig_appmon_info.erl index ccb334a..a842fa6 100644 --- a/src/bigwig_appmon_info.erl +++ b/src/bigwig_appmon_info.erl @@ -370,19 +370,23 @@ find_avoid() -> [undefined], ?IGNORE_APPS). -pid_info({P}) -> +pid_info({P}) -> + Node = node(P), L = - case process_info(P, registered_name) of +% case process_info(P, registered_name) of + case rpc:call(Node,erlang, process_info, [P,registered_name]) of {registered_name, Name} -> [{name, Name}]; _ -> [] end, L1 = - case process_info(P, current_function) of +% case process_info(P, current_function) of + case rpc:call(Node,erlang, process_info, [P,current_function]) of {current_function, CMfa} -> [{cf, tuple_to_list(CMfa)}]; _ -> [] end, L2 = - case process_info(P, dictionary) of +% case process_info(P, dictionary) of + case rpc:call(Node,erlang, process_info, [P,dictionary]) of {dictionary, Dict} when is_list(Dict) -> case lists:keyfind('$initial_call', 1, Dict) of {'$initial_call', IcMfa} -> [{ic, tuple_to_list(IcMfa)}]; @@ -391,7 +395,8 @@ pid_info({P}) -> _ -> [] end, L3 = - case process_info(P, message_queue_len) of +% case process_info(P, message_queue_len) of + case rpc:call(Node,erlang, process_info, [P,message_queue_len]) of {message_queue_len, QLen} -> [{q, QLen}]; _ -> [] end, diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 8c96acf..5bd7717 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -18,6 +18,7 @@ terminate(_Reason, _Req, _State) -> exit(websockets_only). websocket_init(_TransportName, Req, _Opts) -> + io:format("lager_stream init ok~n"), bigwig_pubsubhub:register_client(self()), {ok, Req, undefined_state}. @@ -27,6 +28,7 @@ websocket_handle({text, _Msg}, Req, State) -> {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), + io:format("Reply is ~p",[Reply]), {reply, {text, Reply}, Req, State}; websocket_info({bigwig, _}, Req, State) -> {ok, Req, State}; From a57926f7ae773b8c94c498a0c9b4db46240811c4 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 17:20:00 +0800 Subject: [PATCH 250/405] #5107 tracelog --- priv/js/bigwig.js | 7 ++++++- priv/js/renderer.js | 5 ----- src/bigwig_http_lager_stream.erl | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index b0847cc..b6f5c47 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -64,4 +64,9 @@ function onClose(evt) { function onMessage(evt) { $('#trace_log').prepend('

    ' + evt.data + '

    '); -}; \ No newline at end of file +}; +$("body").delegate("a.tracer", "click", function(e){ + var x1 = $(this).text(); + sendTxt(x1); + e.preventDefault(); +}); \ No newline at end of file diff --git a/priv/js/renderer.js b/priv/js/renderer.js index cc76445..a7cd48a 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -263,11 +263,6 @@ $("body").delegate("a.mfa", "click", function(e){ RENDERER.show_mfa_dialog(x1[0],x2[0],parseInt(x2[1])); e.preventDefault(); }); -$("body").delegate("a.tracer", "click", function(e){ - var x1 = $(this).text(); - sendTxt(x1); - e.preventDefault(); -}); // RENDERER.show_log_dialog(tracer); // e.preventDefault(); diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 5bd7717..eda4828 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -23,12 +23,12 @@ websocket_init(_TransportName, Req, _Opts) -> {ok, Req, undefined_state}. %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too -websocket_handle({text, _Msg}, Req, State) -> +websocket_handle(_Msg, Req, State) -> + io:format("Msg is ~p",[_Msg]), amqp_subscriber:start_link(<<"trace.*">>), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> - Reply = jsx:term_to_json([{lager, Stats}]), - io:format("Reply is ~p",[Reply]), + Reply = jsx:term_to_json([{Stats}]), {reply, {text, Reply}, Req, State}; websocket_info({bigwig, _}, Req, State) -> {ok, Req, State}; From d478b9e8be09b9d9afd514458ced836859cace71 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 17:20:00 +0800 Subject: [PATCH 251/405] #5107 tracelog --- priv/js/bigwig.js | 7 ++++++- priv/js/renderer.js | 5 ----- src/bigwig_http_lager_stream.erl | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index b0847cc..b6f5c47 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -64,4 +64,9 @@ function onClose(evt) { function onMessage(evt) { $('#trace_log').prepend('

    ' + evt.data + '

    '); -}; \ No newline at end of file +}; +$("body").delegate("a.tracer", "click", function(e){ + var x1 = $(this).text(); + sendTxt(x1); + e.preventDefault(); +}); \ No newline at end of file diff --git a/priv/js/renderer.js b/priv/js/renderer.js index cc76445..a7cd48a 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -263,11 +263,6 @@ $("body").delegate("a.mfa", "click", function(e){ RENDERER.show_mfa_dialog(x1[0],x2[0],parseInt(x2[1])); e.preventDefault(); }); -$("body").delegate("a.tracer", "click", function(e){ - var x1 = $(this).text(); - sendTxt(x1); - e.preventDefault(); -}); // RENDERER.show_log_dialog(tracer); // e.preventDefault(); diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 5bd7717..eda4828 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -23,12 +23,12 @@ websocket_init(_TransportName, Req, _Opts) -> {ok, Req, undefined_state}. %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too -websocket_handle({text, _Msg}, Req, State) -> +websocket_handle(_Msg, Req, State) -> + io:format("Msg is ~p",[_Msg]), amqp_subscriber:start_link(<<"trace.*">>), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> - Reply = jsx:term_to_json([{lager, Stats}]), - io:format("Reply is ~p",[Reply]), + Reply = jsx:term_to_json([{Stats}]), {reply, {text, Reply}, Req, State}; websocket_info({bigwig, _}, Req, State) -> {ok, Req, State}; From 081dc617448e8923edec69b7d889e6883de8c4f8 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 17:37:34 +0800 Subject: [PATCH 252/405] #5107 --- priv/html/index.html | 3 ++- src/bigwig_http_lager_stream.erl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index 3817208..2db8dae 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -143,9 +143,10 @@

    BigWig

    +
    - + diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index eda4828..3c37578 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -28,7 +28,7 @@ websocket_handle(_Msg, Req, State) -> amqp_subscriber:start_link(<<"trace.*">>), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> - Reply = jsx:term_to_json([{Stats}]), + Reply = jsx:term_to_json([{lager, Stats}]), {reply, {text, Reply}, Req, State}; websocket_info({bigwig, _}, Req, State) -> {ok, Req, State}; From 960a3a9b02462a379cbcbb8674c47a8527b5c604 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 17:37:34 +0800 Subject: [PATCH 253/405] #5107 --- priv/html/index.html | 3 ++- src/bigwig_http_lager_stream.erl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index 3817208..2db8dae 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -143,9 +143,10 @@

    BigWig

    tracetrace
    +
    - + diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index eda4828..3c37578 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -28,7 +28,7 @@ websocket_handle(_Msg, Req, State) -> amqp_subscriber:start_link(<<"trace.*">>), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> - Reply = jsx:term_to_json([{Stats}]), + Reply = jsx:term_to_json([{lager, Stats}]), {reply, {text, Reply}, Req, State}; websocket_info({bigwig, _}, Req, State) -> {ok, Req, State}; From d7c64f5aca72d600c6449f24bf91ccc006ff9b48 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 18:02:46 +0800 Subject: [PATCH 254/405] #5107 --- priv/js/bigwig.js | 5 +++-- src/bigwig_http_lager_stream.erl | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index b6f5c47..386ccfe 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -66,7 +66,8 @@ function onMessage(evt) { $('#trace_log').prepend('

    ' + evt.data + '

    '); }; $("body").delegate("a.tracer", "click", function(e){ - var x1 = $(this).text(); - sendTxt(x1); + var x1 = $(this).text().split("<<"); + var x2 =x1[1].split(">>"); + sendTxt(x2); e.preventDefault(); }); \ No newline at end of file diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 3c37578..de6a413 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -23,7 +23,7 @@ websocket_init(_TransportName, Req, _Opts) -> {ok, Req, undefined_state}. %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too -websocket_handle(_Msg, Req, State) -> +websocket_handle({text, Msg}, Req, State) -> io:format("Msg is ~p",[_Msg]), amqp_subscriber:start_link(<<"trace.*">>), {ok, Req, State}. From 356209d845ebf7dad803f6c8f3c2c74bbf1234cc Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 18:02:46 +0800 Subject: [PATCH 255/405] #5107 --- priv/js/bigwig.js | 5 +++-- src/bigwig_http_lager_stream.erl | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index b6f5c47..386ccfe 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -66,7 +66,8 @@ function onMessage(evt) { $('#trace_log').prepend('

    ' + evt.data + '

    '); }; $("body").delegate("a.tracer", "click", function(e){ - var x1 = $(this).text(); - sendTxt(x1); + var x1 = $(this).text().split("<<"); + var x2 =x1[1].split(">>"); + sendTxt(x2); e.preventDefault(); }); \ No newline at end of file diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 3c37578..de6a413 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -23,7 +23,7 @@ websocket_init(_TransportName, Req, _Opts) -> {ok, Req, undefined_state}. %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too -websocket_handle(_Msg, Req, State) -> +websocket_handle({text, Msg}, Req, State) -> io:format("Msg is ~p",[_Msg]), amqp_subscriber:start_link(<<"trace.*">>), {ok, Req, State}. From d8a3686f5f1c8ee5bc1267c590ae5418f919b79b Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 18:04:00 +0800 Subject: [PATCH 256/405] #5107 --- src/bigwig_http_lager_stream.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index de6a413..b9989d5 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -24,7 +24,7 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) -> - io:format("Msg is ~p",[_Msg]), + io:format("Msg is ~p",[Msg]), amqp_subscriber:start_link(<<"trace.*">>), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> From 6a6a31425a85b4edc24b068dd76c04aa3da0132e Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 18:04:00 +0800 Subject: [PATCH 257/405] #5107 --- src/bigwig_http_lager_stream.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index de6a413..b9989d5 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -24,7 +24,7 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) -> - io:format("Msg is ~p",[_Msg]), + io:format("Msg is ~p",[Msg]), amqp_subscriber:start_link(<<"trace.*">>), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> From 696379d58beba45d23ac595c4554bf7155baa090 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 18:35:53 +0800 Subject: [PATCH 258/405] #5107 modify the format --- priv/js/bigwig.js | 3 ++- src/bigwig.erl | 2 +- src/bigwig_http_lager_stream.erl | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 386ccfe..77276eb 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -68,6 +68,7 @@ function onMessage(evt) { $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); var x2 =x1[1].split(">>"); - sendTxt(x2); + var x3 = x2[0].replace(/\"/g, ""); + sendTxt(x3); e.preventDefault(); }); \ No newline at end of file diff --git a/src/bigwig.erl b/src/bigwig.erl index 6fdee28..7f8cda4 100644 --- a/src/bigwig.erl +++ b/src/bigwig.erl @@ -9,7 +9,7 @@ start() -> application:ensure_started(sasl), application:ensure_started(cowlib), application:ensure_started(cowboy), - application:ensure_started(lager), + %application:ensure_started(lager), application:start(bigwig). stop() -> diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index b9989d5..8074b2f 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -25,7 +25,7 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) -> io:format("Msg is ~p",[Msg]), - amqp_subscriber:start_link(<<"trace.*">>), + amqp_subscriber:start_link(Msg), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), From 2a5e8a22980e0062fa4fd656346c6c98996fcbe8 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 22 Nov 2013 18:35:53 +0800 Subject: [PATCH 259/405] #5107 modify the format --- priv/js/bigwig.js | 3 ++- src/bigwig.erl | 2 +- src/bigwig_http_lager_stream.erl | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 386ccfe..77276eb 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -68,6 +68,7 @@ function onMessage(evt) { $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); var x2 =x1[1].split(">>"); - sendTxt(x2); + var x3 = x2[0].replace(/\"/g, ""); + sendTxt(x3); e.preventDefault(); }); \ No newline at end of file diff --git a/src/bigwig.erl b/src/bigwig.erl index 6fdee28..7f8cda4 100644 --- a/src/bigwig.erl +++ b/src/bigwig.erl @@ -9,7 +9,7 @@ start() -> application:ensure_started(sasl), application:ensure_started(cowlib), application:ensure_started(cowboy), - application:ensure_started(lager), + %application:ensure_started(lager), application:start(bigwig). stop() -> diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index b9989d5..8074b2f 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -25,7 +25,7 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) -> io:format("Msg is ~p",[Msg]), - amqp_subscriber:start_link(<<"trace.*">>), + amqp_subscriber:start_link(Msg), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), From d97d8f529c431eb42bbfb85a14bce023fd0cea0c Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 11:37:09 +0800 Subject: [PATCH 260/405] #5107 modify the trace log format --- priv/js/bigwig.js | 11 ++++++++++- priv/js/renderer.js | 43 ------------------------------------------- 2 files changed, 10 insertions(+), 44 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 77276eb..648a3ad 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -58,12 +58,21 @@ function onOpen(evt) { }; function sendTxt(txt) { websocket.send(txt); + $('
    ') + .dialog({ + width: 550, + title: 'routingkey(' + txt + ')   ', + buttons: { + "Close": function() { $(this).dialog("close"); }, + "Stop Trace": function() {} + } + }) }; function onClose(evt) { }; function onMessage(evt) { - $('#trace_log').prepend('

    ' + evt.data + '

    '); + $('#trace_dialog').prepend('
    ' + evt.data + ''); }; $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); diff --git a/priv/js/renderer.js b/priv/js/renderer.js index a7cd48a..50c838f 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -45,49 +45,6 @@ var RENDERER = (function() { }); }, - undefined:function(json) - { - $('
    ') - .append( gen_pid_html(json) ) - .dialog({ - title: 'process_info(' + Pid + ')   ' + - '(json)', - width: 550, - buttons: { - "Close": function() { $(this).dialog("close"); }, - "Send Msg": function() { - var msg = prompt("Enter Erlang term to send to <" + Pid + ">"); - if(msg) - { - var thisdiag = $(this); - $.ajax({ - url:url, - type:'POST', - data: {msg: msg}, - success: function(resp){ - alert('Sent ok'); - } - }) - } - }, - "Kill" : function() { - if(confirm("Sure you want to kill <" + Pid + ">")) - { - var thisdiag = $(this); - $.ajax({ - url:url, - type:'DELETE', - success: function(resp){ - thisdiag.dialog("close"); - } - }) - } - } - } - }); - - } - }); } From 7e862098b3c0dab0359ec0f71b6c1e76b11eaf93 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 11:37:09 +0800 Subject: [PATCH 261/405] #5107 modify the trace log format --- priv/js/bigwig.js | 11 ++++++++++- priv/js/renderer.js | 43 ------------------------------------------- 2 files changed, 10 insertions(+), 44 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 77276eb..648a3ad 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -58,12 +58,21 @@ function onOpen(evt) { }; function sendTxt(txt) { websocket.send(txt); + $('
    ') + .dialog({ + width: 550, + title: 'routingkey(' + txt + ')   ', + buttons: { + "Close": function() { $(this).dialog("close"); }, + "Stop Trace": function() {} + } + }) }; function onClose(evt) { }; function onMessage(evt) { - $('#trace_log').prepend('

    ' + evt.data + '

    '); + $('#trace_dialog').prepend('
    ' + evt.data + ''); }; $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); diff --git a/priv/js/renderer.js b/priv/js/renderer.js index a7cd48a..50c838f 100644 --- a/priv/js/renderer.js +++ b/priv/js/renderer.js @@ -45,49 +45,6 @@ var RENDERER = (function() { }); }, - undefined:function(json) - { - $('
    ') - .append( gen_pid_html(json) ) - .dialog({ - title: 'process_info(' + Pid + ')   ' + - '(json)', - width: 550, - buttons: { - "Close": function() { $(this).dialog("close"); }, - "Send Msg": function() { - var msg = prompt("Enter Erlang term to send to <" + Pid + ">"); - if(msg) - { - var thisdiag = $(this); - $.ajax({ - url:url, - type:'POST', - data: {msg: msg}, - success: function(resp){ - alert('Sent ok'); - } - }) - } - }, - "Kill" : function() { - if(confirm("Sure you want to kill <" + Pid + ">")) - { - var thisdiag = $(this); - $.ajax({ - url:url, - type:'DELETE', - success: function(resp){ - thisdiag.dialog("close"); - } - }) - } - } - } - }); - - } - }); } From bf7a9d756a963d3e132932ac0e8b18259218cb8d Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 13:31:35 +0800 Subject: [PATCH 262/405] #5107 --- priv/js/bigwig.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 648a3ad..4c6319d 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -61,6 +61,7 @@ function sendTxt(txt) { $('
    ') .dialog({ width: 550, + height: 800, title: 'routingkey(' + txt + ')   ', buttons: { "Close": function() { $(this).dialog("close"); }, @@ -72,7 +73,7 @@ function onClose(evt) { }; function onMessage(evt) { - $('#trace_dialog').prepend('
    ' + evt.data + ''); + $('#trace_dialog').prepend('

    ' + evt.data + '

    '); }; $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); From 86751b05e026fab95cff0d0dd0eb2afc41583f25 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 13:31:35 +0800 Subject: [PATCH 263/405] #5107 --- priv/js/bigwig.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 648a3ad..4c6319d 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -61,6 +61,7 @@ function sendTxt(txt) { $('
    ') .dialog({ width: 550, + height: 800, title: 'routingkey(' + txt + ')   ', buttons: { "Close": function() { $(this).dialog("close"); }, @@ -72,7 +73,7 @@ function onClose(evt) { }; function onMessage(evt) { - $('#trace_dialog').prepend('
    ' + evt.data + ''); + $('#trace_dialog').prepend('

    ' + evt.data + '

    '); }; $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); From d518f0056972b8f5d5258054a13262dbc6ce6b1f Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 15:16:21 +0800 Subject: [PATCH 264/405] #5107 add trace --- priv/html/index.html | 7 ++----- priv/js/bigwig.js | 5 ++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index 2db8dae..2b2232b 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -134,12 +134,9 @@

    BigWig

    tracetrace
    -
    -
    - +
    Tracer: - - +
    diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 4c6319d..0c02b75 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -61,8 +61,11 @@ function sendTxt(txt) { $('
    ') .dialog({ width: 550, - height: 800, + height: 650, title: 'routingkey(' + txt + ')   ', + close: function(event, ui) { + window.location.reload(); + }, buttons: { "Close": function() { $(this).dialog("close"); }, "Stop Trace": function() {} From d41192c71cffd59aced7a5a0726c763a9f63fd9d Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 15:16:21 +0800 Subject: [PATCH 265/405] #5107 add trace --- priv/html/index.html | 7 ++----- priv/js/bigwig.js | 5 ++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index 2db8dae..2b2232b 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -134,12 +134,9 @@

    BigWig

    -
    -
    -
    +
    Tracer: - - +
    diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 4c6319d..0c02b75 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -61,8 +61,11 @@ function sendTxt(txt) { $('
    ') .dialog({ width: 550, - height: 800, + height: 650, title: 'routingkey(' + txt + ')   ', + close: function(event, ui) { + window.location.reload(); + }, buttons: { "Close": function() { $(this).dialog("close"); }, "Stop Trace": function() {} From d1657c9d47dbdb18f19a3d3043180f9f48826f41 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 15:27:18 +0800 Subject: [PATCH 266/405] #5107 add trace --- priv/html/index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index 2b2232b..140840a 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -134,7 +134,8 @@

    BigWig

    -
    +
    +
    Tracer:
    From 6652810dd8f9062ef8ef667aecac68ad1caa64f0 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 15:27:18 +0800 Subject: [PATCH 267/405] #5107 add trace --- priv/html/index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index 2b2232b..140840a 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -134,7 +134,8 @@

    BigWig

    -
    +
    +
    Tracer:
    From 83742984f99a550bf60813e925ca3c584d00e71b Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 15:48:02 +0800 Subject: [PATCH 268/405] #5107 --- priv/js/bigwig.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 0c02b75..ea6a170 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -43,6 +43,10 @@ $(function() { var result1=result.replace(/ /g,""); $('#lager_status').append($('
    ' + result1 + '
    ')); }); + $('#start_trace').bind('click', function(event) { + var trace=$('#tracer').text(); + alert(trace); + }); connect("/lager/stream"); }); function connect(to) From f9d5144e5d1389809a5e9c6735d4e31ac8d06d42 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 15:48:02 +0800 Subject: [PATCH 269/405] #5107 --- priv/js/bigwig.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 0c02b75..ea6a170 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -43,6 +43,10 @@ $(function() { var result1=result.replace(/ /g,""); $('#lager_status').append($('
    ' + result1 + '
    ')); }); + $('#start_trace').bind('click', function(event) { + var trace=$('#tracer').text(); + alert(trace); + }); connect("/lager/stream"); }); function connect(to) From 79fecba65aeddd654c69d3a2c263558fb3f05f66 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 17:21:07 +0800 Subject: [PATCH 270/405] #5107 add tracer --- priv/js/bigwig.js | 21 +++++++++++++++++++-- src/bigwig_http_lager.erl | 14 ++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index ea6a170..4e26bf6 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -44,8 +44,25 @@ $(function() { $('#lager_status').append($('
    ' + result1 + '
    ')); }); $('#start_trace').bind('click', function(event) { - var trace=$('#tracer').text(); - alert(trace); + var trace=$('#tracer').val(); + var x = trace.split(":"); + var tracer = '{'; + for(var i=0; i< x.length; ++i) + { + tracer = tracer + x[i] + ','; + } + tracer = tracer.substring(0,tracer.length-1); + tracer = tracer + '}'; + + url = '/lager/tracer/'+tracer; + + $.ajax({ + url:url, + type:'PUT', + success: function(resp){ + alert('Add Tracer success'); + } + }) }); connect("/lager/stream"); }); diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 009248e..721fbcc 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -20,7 +20,7 @@ handle_path(<<"GET">>, [<<"lager">>, <<"status">>], Req, State) -> handle_path(<<"GET">>, [<<"lager">>, <<"tracer">>, RoutingKey], Req, State) -> handle_get_log(RoutingKey, Req, State); handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> - not_found(Req, State); + handle_add_tracer(Tracer, Req, State); handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> not_found(Req, State); handle_path(_, _, Req, State) -> @@ -34,13 +34,23 @@ handle_get_status(Req,State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_get_log(RoutingKey, Req, State) -> -% amqp_subscriber:start_link(<<"trace.*">>), {ok,Info}=file:read_file("trace.log"), Msg=[{bigwig_trace, Info}], Body = jsx:term_to_json(Msg), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. +handle_add_tracer(Tracer, Req, State) -> +io:format("tracer is ~p", [Tracer]), + case Tracer of + {distributed, RoutingKey, Filter, Level} -> + amqp_tracer:trace_amqp(distributed, RoutingKey, Filter, Level); + {RoutingKey, Filter} -> + amqp_tracer:trace_amqp(RoutingKey, Filter); + {RoutingKey, Filter, Level} -> + amqp_tracer:trace_amqp(RoutingKey, Filter, Level) + end, + {ok,Req,State}. not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. From bba54220f58b8c0f8814988d334ceb78c2d3fafe Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 17:21:07 +0800 Subject: [PATCH 271/405] #5107 add tracer --- priv/js/bigwig.js | 21 +++++++++++++++++++-- src/bigwig_http_lager.erl | 14 ++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index ea6a170..4e26bf6 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -44,8 +44,25 @@ $(function() { $('#lager_status').append($('
    ' + result1 + '
    ')); }); $('#start_trace').bind('click', function(event) { - var trace=$('#tracer').text(); - alert(trace); + var trace=$('#tracer').val(); + var x = trace.split(":"); + var tracer = '{'; + for(var i=0; i< x.length; ++i) + { + tracer = tracer + x[i] + ','; + } + tracer = tracer.substring(0,tracer.length-1); + tracer = tracer + '}'; + + url = '/lager/tracer/'+tracer; + + $.ajax({ + url:url, + type:'PUT', + success: function(resp){ + alert('Add Tracer success'); + } + }) }); connect("/lager/stream"); }); diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 009248e..721fbcc 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -20,7 +20,7 @@ handle_path(<<"GET">>, [<<"lager">>, <<"status">>], Req, State) -> handle_path(<<"GET">>, [<<"lager">>, <<"tracer">>, RoutingKey], Req, State) -> handle_get_log(RoutingKey, Req, State); handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> - not_found(Req, State); + handle_add_tracer(Tracer, Req, State); handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> not_found(Req, State); handle_path(_, _, Req, State) -> @@ -34,13 +34,23 @@ handle_get_status(Req,State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_get_log(RoutingKey, Req, State) -> -% amqp_subscriber:start_link(<<"trace.*">>), {ok,Info}=file:read_file("trace.log"), Msg=[{bigwig_trace, Info}], Body = jsx:term_to_json(Msg), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. +handle_add_tracer(Tracer, Req, State) -> +io:format("tracer is ~p", [Tracer]), + case Tracer of + {distributed, RoutingKey, Filter, Level} -> + amqp_tracer:trace_amqp(distributed, RoutingKey, Filter, Level); + {RoutingKey, Filter} -> + amqp_tracer:trace_amqp(RoutingKey, Filter); + {RoutingKey, Filter, Level} -> + amqp_tracer:trace_amqp(RoutingKey, Filter, Level) + end, + {ok,Req,State}. not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. From 856bae521fbb9d44dabedbb11decf038c26ef8f4 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 17:28:41 +0800 Subject: [PATCH 272/405] #5107 --- src/bigwig_http_lager.erl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 721fbcc..ea11dee 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -42,13 +42,17 @@ handle_get_log(RoutingKey, Req, State) -> {ok, Req2, State}. handle_add_tracer(Tracer, Req, State) -> io:format("tracer is ~p", [Tracer]), +amqp_tracer:start_link(), case Tracer of {distributed, RoutingKey, Filter, Level} -> - amqp_tracer:trace_amqp(distributed, RoutingKey, Filter, Level); + RoutingKey1=atom_to_binary(RoutingKey,utf8), + amqp_tracer:trace_amqp(distributed, RoutingKey1, Filter, Level); {RoutingKey, Filter} -> - amqp_tracer:trace_amqp(RoutingKey, Filter); + RoutingKey1=atom_to_binary(RoutingKey,utf8), + amqp_tracer:trace_amqp(RoutingKey1, Filter); {RoutingKey, Filter, Level} -> - amqp_tracer:trace_amqp(RoutingKey, Filter, Level) + RoutingKey1=atom_to_binary(RoutingKey,utf8), + amqp_tracer:trace_amqp(RoutingKey1, Filter, Level) end, {ok,Req,State}. not_found(Req, State) -> From 36701bcdbd0a4e8344903a9f023f5906265da1ae Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 17:28:41 +0800 Subject: [PATCH 273/405] #5107 --- src/bigwig_http_lager.erl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 721fbcc..ea11dee 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -42,13 +42,17 @@ handle_get_log(RoutingKey, Req, State) -> {ok, Req2, State}. handle_add_tracer(Tracer, Req, State) -> io:format("tracer is ~p", [Tracer]), +amqp_tracer:start_link(), case Tracer of {distributed, RoutingKey, Filter, Level} -> - amqp_tracer:trace_amqp(distributed, RoutingKey, Filter, Level); + RoutingKey1=atom_to_binary(RoutingKey,utf8), + amqp_tracer:trace_amqp(distributed, RoutingKey1, Filter, Level); {RoutingKey, Filter} -> - amqp_tracer:trace_amqp(RoutingKey, Filter); + RoutingKey1=atom_to_binary(RoutingKey,utf8), + amqp_tracer:trace_amqp(RoutingKey1, Filter); {RoutingKey, Filter, Level} -> - amqp_tracer:trace_amqp(RoutingKey, Filter, Level) + RoutingKey1=atom_to_binary(RoutingKey,utf8), + amqp_tracer:trace_amqp(RoutingKey1, Filter, Level) end, {ok,Req,State}. not_found(Req, State) -> From 68d80dbbf47e22995328884be6a6f07b39799b2e Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 18:42:08 +0800 Subject: [PATCH 274/405] #5107 --- priv/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index 140840a..e331ecf 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -137,7 +137,7 @@

    BigWig

    Tracer: - +
    From a09b39ddb89522da7e5b3bfcb812109bdbd5556e Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 25 Nov 2013 18:42:08 +0800 Subject: [PATCH 275/405] #5107 --- priv/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index 140840a..e331ecf 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -137,7 +137,7 @@

    BigWig

    Tracer: - +
    From 32800d26432919aeffc650f4a701f9819894c3ba Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 13:20:34 +0800 Subject: [PATCH 276/405] #5107 modify format --- priv/js/bigwig.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 4e26bf6..a0b1842 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -80,6 +80,17 @@ function onOpen(evt) { function sendTxt(txt) { websocket.send(txt); $('
    ') + .append( $('
    ') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ); .dialog({ width: 550, height: 650, @@ -97,7 +108,22 @@ function onClose(evt) { }; function onMessage(evt) { - $('#trace_dialog').prepend('

    ' + evt.data + '

    '); + var x = (evt.data).split(":\""); + var x1 = x[1].split(" "); + var x2 = ""; + for(var i=4; i < x1.length; ++i) + { + x2 = x2+ x1[i]; + } + $('#trace_dialog').prepend( $('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ); }; $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); From 8ff3190c69528d5f210dda26c44155b6674e820c Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 13:20:34 +0800 Subject: [PATCH 277/405] #5107 modify format --- priv/js/bigwig.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 4e26bf6..a0b1842 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -80,6 +80,17 @@ function onOpen(evt) { function sendTxt(txt) { websocket.send(txt); $('
    ') + .append( $('
    ') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ); .dialog({ width: 550, height: 650, @@ -97,7 +108,22 @@ function onClose(evt) { }; function onMessage(evt) { - $('#trace_dialog').prepend('

    ' + evt.data + '

    '); + var x = (evt.data).split(":\""); + var x1 = x[1].split(" "); + var x2 = ""; + for(var i=4; i < x1.length; ++i) + { + x2 = x2+ x1[i]; + } + $('#trace_dialog').prepend( $('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ); }; $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); From fbb8a6852f76a80199d5be03428de04329fa3c3a Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 13:26:54 +0800 Subject: [PATCH 278/405] #5107 modify format --- priv/js/bigwig.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index a0b1842..66ba419 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -81,8 +81,6 @@ function sendTxt(txt) { websocket.send(txt); $('
    ') .append( $('
    ') - .append('') - .append('') .append('') .append('') .append('') @@ -90,7 +88,7 @@ function sendTxt(txt) { .append('') .append('') .append('') - ); + ) .dialog({ width: 550, height: 650, @@ -115,8 +113,8 @@ function onMessage(evt) { { x2 = x2+ x1[i]; } - $('#trace_dialog').prepend( $('') - .append('') + $('#trace_dialog').append( $('') + .append('') .append('') .append('') .append('') From e1a03d3e12ef552b23d39b834a6dd6da4bd91ee0 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 13:26:54 +0800 Subject: [PATCH 279/405] #5107 modify format --- priv/js/bigwig.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index a0b1842..66ba419 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -81,8 +81,6 @@ function sendTxt(txt) { websocket.send(txt); $('
    ') .append( $('
    ') - .append('') - .append('') .append('') .append('') .append('') @@ -90,7 +88,7 @@ function sendTxt(txt) { .append('') .append('') .append('') - ); + ) .dialog({ width: 550, height: 650, @@ -115,8 +113,8 @@ function onMessage(evt) { { x2 = x2+ x1[i]; } - $('#trace_dialog').prepend( $('') - .append('') + $('#trace_dialog').append( $('') + .append('') .append('') .append('') .append('') From a527211011e0238e7309af3f108d4b4db953bedc Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 13:38:08 +0800 Subject: [PATCH 280/405] #5107 modify the input text length --- priv/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index e331ecf..caccf6e 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -136,7 +136,7 @@

    BigWig

    - Tracer: + Tracer:
    From de769bac297261ef7470997774d32915f1a099df Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 13:38:08 +0800 Subject: [PATCH 281/405] #5107 modify the input text length --- priv/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index e331ecf..caccf6e 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -136,7 +136,7 @@

    BigWig

    - Tracer: + Tracer:
    From b0163c6309221841a3e7ed57cf395840b276cf80 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 15:38:49 +0800 Subject: [PATCH 282/405] #5107 add tracer --- priv/js/bigwig.js | 2 -- src/bigwig_http_lager.erl | 42 ++++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 66ba419..4df015b 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -46,13 +46,11 @@ $(function() { $('#start_trace').bind('click', function(event) { var trace=$('#tracer').val(); var x = trace.split(":"); - var tracer = '{'; for(var i=0; i< x.length; ++i) { tracer = tracer + x[i] + ','; } tracer = tracer.substring(0,tracer.length-1); - tracer = tracer + '}'; url = '/lager/tracer/'+tracer; diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index ea11dee..1b9d420 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -21,7 +21,7 @@ handle_path(<<"GET">>, [<<"lager">>, <<"tracer">>, RoutingKey], Req, State) -> handle_get_log(RoutingKey, Req, State); handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> handle_add_tracer(Tracer, Req, State); -handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> +handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, _Tracer], Req, State) -> not_found(Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). @@ -33,7 +33,7 @@ handle_get_status(Req,State) -> Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. -handle_get_log(RoutingKey, Req, State) -> +handle_get_log(_RoutingKey, Req, State) -> {ok,Info}=file:read_file("trace.log"), Msg=[{bigwig_trace, Info}], Body = jsx:term_to_json(Msg), @@ -41,20 +41,30 @@ handle_get_log(RoutingKey, Req, State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_add_tracer(Tracer, Req, State) -> -io:format("tracer is ~p", [Tracer]), -amqp_tracer:start_link(), - case Tracer of - {distributed, RoutingKey, Filter, Level} -> - RoutingKey1=atom_to_binary(RoutingKey,utf8), - amqp_tracer:trace_amqp(distributed, RoutingKey1, Filter, Level); - {RoutingKey, Filter} -> - RoutingKey1=atom_to_binary(RoutingKey,utf8), - amqp_tracer:trace_amqp(RoutingKey1, Filter); - {RoutingKey, Filter, Level} -> - RoutingKey1=atom_to_binary(RoutingKey,utf8), - amqp_tracer:trace_amqp(RoutingKey1, Filter, Level) - end, - {ok,Req,State}. + + io:format("tracer is ~p", [Tracer]), + All = binary:split(Tracer,[<<",">>],[global]), + + amqp_tracer:start_link(), + case length(All) of + 5 -> + Attr1 = binary_to_atom(lists:nth(1, All), utf8), + RoutingKey = lists:nth(2, All), + Filter = [{binary_to_atom(lists:nth(3, All), utf8), binary_to_integer(lists:nth(4, All))}], + Level = binary_to_atom(lists:nth(5, All), utf8), + amqp_tracer:trace_amqp(Attr1, RoutingKey, Filter, Level); + 3 -> + RoutingKey = lists:nth(1, All), + Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + amqp_tracer:trace_amqp(RoutingKey, Filter); + 4 -> + RoutingKey = lists:nth(1, All), + Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + Level = binary_to_atom(lists:nth(4, All), utf8), + amqp_tracer:trace_amqp(RoutingKey, Filter, Level); + _ -> ok + end, + {ok,Req,State}. not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. From 869564a106801093671771649bf1b18a4217294b Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 15:38:49 +0800 Subject: [PATCH 283/405] #5107 add tracer --- priv/js/bigwig.js | 2 -- src/bigwig_http_lager.erl | 42 ++++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 66ba419..4df015b 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -46,13 +46,11 @@ $(function() { $('#start_trace').bind('click', function(event) { var trace=$('#tracer').val(); var x = trace.split(":"); - var tracer = '{'; for(var i=0; i< x.length; ++i) { tracer = tracer + x[i] + ','; } tracer = tracer.substring(0,tracer.length-1); - tracer = tracer + '}'; url = '/lager/tracer/'+tracer; diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index ea11dee..1b9d420 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -21,7 +21,7 @@ handle_path(<<"GET">>, [<<"lager">>, <<"tracer">>, RoutingKey], Req, State) -> handle_get_log(RoutingKey, Req, State); handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> handle_add_tracer(Tracer, Req, State); -handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> +handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, _Tracer], Req, State) -> not_found(Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). @@ -33,7 +33,7 @@ handle_get_status(Req,State) -> Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. -handle_get_log(RoutingKey, Req, State) -> +handle_get_log(_RoutingKey, Req, State) -> {ok,Info}=file:read_file("trace.log"), Msg=[{bigwig_trace, Info}], Body = jsx:term_to_json(Msg), @@ -41,20 +41,30 @@ handle_get_log(RoutingKey, Req, State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_add_tracer(Tracer, Req, State) -> -io:format("tracer is ~p", [Tracer]), -amqp_tracer:start_link(), - case Tracer of - {distributed, RoutingKey, Filter, Level} -> - RoutingKey1=atom_to_binary(RoutingKey,utf8), - amqp_tracer:trace_amqp(distributed, RoutingKey1, Filter, Level); - {RoutingKey, Filter} -> - RoutingKey1=atom_to_binary(RoutingKey,utf8), - amqp_tracer:trace_amqp(RoutingKey1, Filter); - {RoutingKey, Filter, Level} -> - RoutingKey1=atom_to_binary(RoutingKey,utf8), - amqp_tracer:trace_amqp(RoutingKey1, Filter, Level) - end, - {ok,Req,State}. + + io:format("tracer is ~p", [Tracer]), + All = binary:split(Tracer,[<<",">>],[global]), + + amqp_tracer:start_link(), + case length(All) of + 5 -> + Attr1 = binary_to_atom(lists:nth(1, All), utf8), + RoutingKey = lists:nth(2, All), + Filter = [{binary_to_atom(lists:nth(3, All), utf8), binary_to_integer(lists:nth(4, All))}], + Level = binary_to_atom(lists:nth(5, All), utf8), + amqp_tracer:trace_amqp(Attr1, RoutingKey, Filter, Level); + 3 -> + RoutingKey = lists:nth(1, All), + Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + amqp_tracer:trace_amqp(RoutingKey, Filter); + 4 -> + RoutingKey = lists:nth(1, All), + Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + Level = binary_to_atom(lists:nth(4, All), utf8), + amqp_tracer:trace_amqp(RoutingKey, Filter, Level); + _ -> ok + end, + {ok,Req,State}. not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. From 4e77b948e707cb776076d09e6aef1c462fd1fa99 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 15:49:05 +0800 Subject: [PATCH 284/405] #5107 --- priv/js/bigwig.js | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 4df015b..2774a1c 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -46,6 +46,7 @@ $(function() { $('#start_trace').bind('click', function(event) { var trace=$('#tracer').val(); var x = trace.split(":"); + var tracer = ''; for(var i=0; i< x.length; ++i) { tracer = tracer + x[i] + ','; From c59a3107c981f47ed4049e021ff0a11248c865e4 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 15:49:05 +0800 Subject: [PATCH 285/405] #5107 --- priv/js/bigwig.js | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 4df015b..2774a1c 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -46,6 +46,7 @@ $(function() { $('#start_trace').bind('click', function(event) { var trace=$('#tracer').val(); var x = trace.split(":"); + var tracer = ''; for(var i=0; i< x.length; ++i) { tracer = tracer + x[i] + ','; From 8dd4f791df50e786d5ca9012edb711b86dd8ec49 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 16:12:11 +0800 Subject: [PATCH 286/405] #5107 remove some testcode --- priv/js/bigwig.js | 3 ++- src/bigwig_http_lager_stream.erl | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 2774a1c..93aca02 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -59,7 +59,8 @@ $(function() { url:url, type:'PUT', success: function(resp){ - alert('Add Tracer success'); + alert('Add tracer success'); + window.location.reload(); } }) }); diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 8074b2f..0d95d62 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -18,13 +18,11 @@ terminate(_Reason, _Req, _State) -> exit(websockets_only). websocket_init(_TransportName, Req, _Opts) -> - io:format("lager_stream init ok~n"), bigwig_pubsubhub:register_client(self()), {ok, Req, undefined_state}. %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) -> - io:format("Msg is ~p",[Msg]), amqp_subscriber:start_link(Msg), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> From 640618d26bd22dbed5bdbdd6ed68215385a5cf1a Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 16:12:11 +0800 Subject: [PATCH 287/405] #5107 remove some testcode --- priv/js/bigwig.js | 3 ++- src/bigwig_http_lager_stream.erl | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 2774a1c..93aca02 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -59,7 +59,8 @@ $(function() { url:url, type:'PUT', success: function(resp){ - alert('Add Tracer success'); + alert('Add tracer success'); + window.location.reload(); } }) }); diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 8074b2f..0d95d62 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -18,13 +18,11 @@ terminate(_Reason, _Req, _State) -> exit(websockets_only). websocket_init(_TransportName, Req, _Opts) -> - io:format("lager_stream init ok~n"), bigwig_pubsubhub:register_client(self()), {ok, Req, undefined_state}. %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) -> - io:format("Msg is ~p",[Msg]), amqp_subscriber:start_link(Msg), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> From 626a27a4d821d4f55e9e595d76c0577341e73ea1 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 16:17:50 +0800 Subject: [PATCH 288/405] #5107 clear traces --- priv/html/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/html/index.html b/priv/html/index.html index caccf6e..220af14 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -138,6 +138,7 @@

    BigWig

    Tracer: +
    '+"Date"+' '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+'
    '+x1[0]+" "+x1[1]+' '+x1[2]+' '+x1[3]+' '+x2+'
    '+"Date"+' '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+'
    '+x1[0]+" "+x1[1]+' '+x1[2]+' '+x1[3]+' '+x2+'
    '+"Date"+' '+"Time"+' '+"Node"+''+"Level"+' '+"Message"+'
    '+x1[0]+" "+x1[1]+'
    '+x1[0]+x1[1]+' '+x1[2]+'
    '+"Date"+' '+"Time"+' '+"Node"+''+"Level"+' '+"Message"+'
    '+x1[0]+" "+x1[1]+'
    '+x1[0]+x1[1]+' '+x1[2]+'
    From f4aca05a9c0fd68f844995c945bd1f38d0370ad9 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 16:17:50 +0800 Subject: [PATCH 289/405] #5107 clear traces --- priv/html/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/html/index.html b/priv/html/index.html index caccf6e..220af14 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -138,6 +138,7 @@

    BigWig

    Tracer: +
    From 20a07607242696c4b8b08be094eaf077c6fa5746 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 16:47:13 +0800 Subject: [PATCH 290/405] #5107 clear all traces --- priv/js/bigwig.js | 11 +++++++++++ src/bigwig_http_lager.erl | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 93aca02..1f8ee85 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -64,6 +64,17 @@ $(function() { } }) }); + $('#clear_all_traces').bind('click', function(event) { + url = '/lager/tracer/all', + $.ajax({ + url:url, + type:'DELETE', + success: function(resp){ + alert('Clear all traces success'); + window.location.reload(); + } + }) + }; connect("/lager/stream"); }); function connect(to) diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 1b9d420..401da41 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -21,6 +21,8 @@ handle_path(<<"GET">>, [<<"lager">>, <<"tracer">>, RoutingKey], Req, State) -> handle_get_log(RoutingKey, Req, State); handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> handle_add_tracer(Tracer, Req, State); +handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, <<"all">>], Req, State) -> + handle_clear_traces(Req, State); handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, _Tracer], Req, State) -> not_found(Req, State); handle_path(_, _, Req, State) -> @@ -65,6 +67,9 @@ handle_add_tracer(Tracer, Req, State) -> _ -> ok end, {ok,Req,State}. +handle_clear_traces(Req, State) -> + lager:clear_all_traces(), + {ok,Req,State}. not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. From 19872d425d0693bdb57fb0cb79aed931f79dadda Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 16:47:13 +0800 Subject: [PATCH 291/405] #5107 clear all traces --- priv/js/bigwig.js | 11 +++++++++++ src/bigwig_http_lager.erl | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 93aca02..1f8ee85 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -64,6 +64,17 @@ $(function() { } }) }); + $('#clear_all_traces').bind('click', function(event) { + url = '/lager/tracer/all', + $.ajax({ + url:url, + type:'DELETE', + success: function(resp){ + alert('Clear all traces success'); + window.location.reload(); + } + }) + }; connect("/lager/stream"); }); function connect(to) diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 1b9d420..401da41 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -21,6 +21,8 @@ handle_path(<<"GET">>, [<<"lager">>, <<"tracer">>, RoutingKey], Req, State) -> handle_get_log(RoutingKey, Req, State); handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> handle_add_tracer(Tracer, Req, State); +handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, <<"all">>], Req, State) -> + handle_clear_traces(Req, State); handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, _Tracer], Req, State) -> not_found(Req, State); handle_path(_, _, Req, State) -> @@ -65,6 +67,9 @@ handle_add_tracer(Tracer, Req, State) -> _ -> ok end, {ok,Req,State}. +handle_clear_traces(Req, State) -> + lager:clear_all_traces(), + {ok,Req,State}. not_found(Req, State) -> {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), {ok, Req2, State}. From f8b712ec730c179c4303e22d772379b39214d969 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 16:50:52 +0800 Subject: [PATCH 292/405] #5107 clear all traces --- priv/js/bigwig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 1f8ee85..fcce8b6 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -65,7 +65,7 @@ $(function() { }) }); $('#clear_all_traces').bind('click', function(event) { - url = '/lager/tracer/all', + url = '/lager/tracer/all'; $.ajax({ url:url, type:'DELETE', From a3b3c5724d6d119af890669f524439b051b57c2e Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 16:50:52 +0800 Subject: [PATCH 293/405] #5107 clear all traces --- priv/js/bigwig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 1f8ee85..fcce8b6 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -65,7 +65,7 @@ $(function() { }) }); $('#clear_all_traces').bind('click', function(event) { - url = '/lager/tracer/all', + url = '/lager/tracer/all'; $.ajax({ url:url, type:'DELETE', From 72bfc80f550d73c5a3625525ef7f3b2f44083759 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 16:58:13 +0800 Subject: [PATCH 294/405] #5107 clear all traces --- priv/js/bigwig.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index fcce8b6..41225de 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -53,7 +53,7 @@ $(function() { } tracer = tracer.substring(0,tracer.length-1); - url = '/lager/tracer/'+tracer; + var url = '/lager/tracer/'+tracer; $.ajax({ url:url, @@ -65,9 +65,9 @@ $(function() { }) }); $('#clear_all_traces').bind('click', function(event) { - url = '/lager/tracer/all'; + var url1 = '/lager/tracer/all'; $.ajax({ - url:url, + url:url1, type:'DELETE', success: function(resp){ alert('Clear all traces success'); From aa958a7e4df3bc5e7f7ed5313c40b2dc7e63f6a9 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 16:58:13 +0800 Subject: [PATCH 295/405] #5107 clear all traces --- priv/js/bigwig.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index fcce8b6..41225de 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -53,7 +53,7 @@ $(function() { } tracer = tracer.substring(0,tracer.length-1); - url = '/lager/tracer/'+tracer; + var url = '/lager/tracer/'+tracer; $.ajax({ url:url, @@ -65,9 +65,9 @@ $(function() { }) }); $('#clear_all_traces').bind('click', function(event) { - url = '/lager/tracer/all'; + var url1 = '/lager/tracer/all'; $.ajax({ - url:url, + url:url1, type:'DELETE', success: function(resp){ alert('Clear all traces success'); From 6d6cf0577f5cd812331910b3e58260efbc57bd03 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 17:02:10 +0800 Subject: [PATCH 296/405] #5107 clear all traces --- priv/js/bigwig.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 41225de..4575f49 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -65,16 +65,16 @@ $(function() { }) }); $('#clear_all_traces').bind('click', function(event) { - var url1 = '/lager/tracer/all'; + var url = '/lager/tracer/all'; $.ajax({ - url:url1, + url:url, type:'DELETE', success: function(resp){ alert('Clear all traces success'); window.location.reload(); } }) - }; + }); connect("/lager/stream"); }); function connect(to) From c380d368be8d2053deb8a40118b1a1a91a8ebdb2 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 17:02:10 +0800 Subject: [PATCH 297/405] #5107 clear all traces --- priv/js/bigwig.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 41225de..4575f49 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -65,16 +65,16 @@ $(function() { }) }); $('#clear_all_traces').bind('click', function(event) { - var url1 = '/lager/tracer/all'; + var url = '/lager/tracer/all'; $.ajax({ - url:url1, + url:url, type:'DELETE', success: function(resp){ alert('Clear all traces success'); window.location.reload(); } }) - }; + }); connect("/lager/stream"); }); function connect(to) From bc940846253fcab58cedc6a200a1b0a79460fb3c Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 17:23:44 +0800 Subject: [PATCH 298/405] #5107 modify color --- priv/js/bigwig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 4575f49..d7ab8ca 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -91,7 +91,7 @@ function onOpen(evt) { function sendTxt(txt) { websocket.send(txt); $('
    ') - .append( $('
    ') + .append( $('') .append('') .append('') .append('') From 22f0ac02839087dfda8083d531a954f63b2fbf3f Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 17:23:44 +0800 Subject: [PATCH 299/405] #5107 modify color --- priv/js/bigwig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 4575f49..d7ab8ca 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -91,7 +91,7 @@ function onOpen(evt) { function sendTxt(txt) { websocket.send(txt); $('
    ') - .append( $('
    ') + .append( $('') .append('') .append('') .append('') From d3cc5d6848800c41d2714a3d6ac0c8faf039f193 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 18:06:45 +0800 Subject: [PATCH 300/405] #5107 modify the dialog style --- priv/js/bigwig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index d7ab8ca..4575f49 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -91,7 +91,7 @@ function onOpen(evt) { function sendTxt(txt) { websocket.send(txt); $('
    ') - .append( $('
    ') + .append( $('') .append('') .append('') .append('') From 6e33bd63bf785752feb2b8fc87729d445644c907 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 26 Nov 2013 18:06:45 +0800 Subject: [PATCH 301/405] #5107 modify the dialog style --- priv/js/bigwig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index d7ab8ca..4575f49 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -91,7 +91,7 @@ function onOpen(evt) { function sendTxt(txt) { websocket.send(txt); $('
    ') - .append( $('
    ') + .append( $('') .append('') .append('') .append('') From b2250f8155970b9979608d190417c3eff53ae66c Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 27 Nov 2013 11:08:10 +0800 Subject: [PATCH 302/405] #5107 unsubscribe --- priv/js/bigwig.js | 46 +++++++++++++++++--------------- src/bigwig_http_lager.erl | 7 +---- src/bigwig_http_lager_stream.erl | 13 ++++++--- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 4575f49..8ab8d24 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -88,6 +88,26 @@ function connect(to) function onOpen(evt) { }; +function onClose(evt) { +}; +function onMessage(evt) { + var x = (evt.data).split(":\""); + var x1 = x[1].split(" "); + var x2 = ""; + for(var i=4; i < x1.length; ++i) + { + x2 = x2+ x1[i]; + } + $('#trace_dialog').append( $('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ); +}; function sendTxt(txt) { websocket.send(txt); $('
    ') @@ -105,7 +125,7 @@ function sendTxt(txt) { height: 650, title: 'routingkey(' + txt + ')   ', close: function(event, ui) { - window.location.reload(); + closeSubscribe(); }, buttons: { "Close": function() { $(this).dialog("close"); }, @@ -113,27 +133,9 @@ function sendTxt(txt) { } }) }; -function onClose(evt) { -}; - -function onMessage(evt) { - var x = (evt.data).split(":\""); - var x1 = x[1].split(" "); - var x2 = ""; - for(var i=4; i < x1.length; ++i) - { - x2 = x2+ x1[i]; - } - $('#trace_dialog').append( $('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - ); -}; +function closeSubscribe(){ + websocket.send('kill'); +} $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); var x2 =x1[1].split(">>"); diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 401da41..77c45d0 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -36,12 +36,7 @@ handle_get_status(Req,State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_get_log(_RoutingKey, Req, State) -> - {ok,Info}=file:read_file("trace.log"), - Msg=[{bigwig_trace, Info}], - Body = jsx:term_to_json(Msg), - Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), - {ok, Req2, State}. + {ok, Req, State}. handle_add_tracer(Tracer, Req, State) -> io:format("tracer is ~p", [Tracer]), diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 0d95d62..257c75b 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -6,7 +6,7 @@ -export([init/3, handle/2, terminate/3]). -export([websocket_init/3, websocket_handle/3, websocket_info/3, websocket_terminate/3]). - +-record(state, {pid}). init({tcp, http}, _Req, _Opts) -> {upgrade, protocol, cowboy_websocket}. @@ -23,8 +23,15 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) -> - amqp_subscriber:start_link(Msg), - {ok, Req, State}. + case Msg of + <<"kill">> -> + Pid = State#state.pid, + amqp_channel:close(Pid), + {ok, Req, State}; + _ -> + {ok, Pid}=amqp_subscriber:start_link(Msg), + {ok, Req, State#state{ pid = Pid }} + end. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), {reply, {text, Reply}, Req, State}; From 2cef852e4d5412329aedac1e0ca75206af717547 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 27 Nov 2013 11:08:10 +0800 Subject: [PATCH 303/405] #5107 unsubscribe --- priv/js/bigwig.js | 46 +++++++++++++++++--------------- src/bigwig_http_lager.erl | 7 +---- src/bigwig_http_lager_stream.erl | 13 ++++++--- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 4575f49..8ab8d24 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -88,6 +88,26 @@ function connect(to) function onOpen(evt) { }; +function onClose(evt) { +}; +function onMessage(evt) { + var x = (evt.data).split(":\""); + var x1 = x[1].split(" "); + var x2 = ""; + for(var i=4; i < x1.length; ++i) + { + x2 = x2+ x1[i]; + } + $('#trace_dialog').append( $('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ); +}; function sendTxt(txt) { websocket.send(txt); $('
    ') @@ -105,7 +125,7 @@ function sendTxt(txt) { height: 650, title: 'routingkey(' + txt + ')   ', close: function(event, ui) { - window.location.reload(); + closeSubscribe(); }, buttons: { "Close": function() { $(this).dialog("close"); }, @@ -113,27 +133,9 @@ function sendTxt(txt) { } }) }; -function onClose(evt) { -}; - -function onMessage(evt) { - var x = (evt.data).split(":\""); - var x1 = x[1].split(" "); - var x2 = ""; - for(var i=4; i < x1.length; ++i) - { - x2 = x2+ x1[i]; - } - $('#trace_dialog').append( $('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - ); -}; +function closeSubscribe(){ + websocket.send('kill'); +} $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); var x2 =x1[1].split(">>"); diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 401da41..77c45d0 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -36,12 +36,7 @@ handle_get_status(Req,State) -> {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. handle_get_log(_RoutingKey, Req, State) -> - {ok,Info}=file:read_file("trace.log"), - Msg=[{bigwig_trace, Info}], - Body = jsx:term_to_json(Msg), - Headers = [{<<"Content-Type">>, <<"application/json">>}], - {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), - {ok, Req2, State}. + {ok, Req, State}. handle_add_tracer(Tracer, Req, State) -> io:format("tracer is ~p", [Tracer]), diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 0d95d62..257c75b 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -6,7 +6,7 @@ -export([init/3, handle/2, terminate/3]). -export([websocket_init/3, websocket_handle/3, websocket_info/3, websocket_terminate/3]). - +-record(state, {pid}). init({tcp, http}, _Req, _Opts) -> {upgrade, protocol, cowboy_websocket}. @@ -23,8 +23,15 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) -> - amqp_subscriber:start_link(Msg), - {ok, Req, State}. + case Msg of + <<"kill">> -> + Pid = State#state.pid, + amqp_channel:close(Pid), + {ok, Req, State}; + _ -> + {ok, Pid}=amqp_subscriber:start_link(Msg), + {ok, Req, State#state{ pid = Pid }} + end. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), {reply, {text, Reply}, Req, State}; From f41c58740b0644ac48e891c8fba4cde7254188fb Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 27 Nov 2013 11:43:06 +0800 Subject: [PATCH 304/405] #5107 Unsubscribe --- src/bigwig_http_lager_stream.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 257c75b..6051bb1 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -28,7 +28,7 @@ websocket_handle({text, Msg}, Req, State) -> Pid = State#state.pid, amqp_channel:close(Pid), {ok, Req, State}; - _ -> + Msg -> {ok, Pid}=amqp_subscriber:start_link(Msg), {ok, Req, State#state{ pid = Pid }} end. From 7cb10795a42fd6e8ecb3449795d57e8a76beaf6c Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 27 Nov 2013 11:43:06 +0800 Subject: [PATCH 305/405] #5107 Unsubscribe --- src/bigwig_http_lager_stream.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 257c75b..6051bb1 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -28,7 +28,7 @@ websocket_handle({text, Msg}, Req, State) -> Pid = State#state.pid, amqp_channel:close(Pid), {ok, Req, State}; - _ -> + Msg -> {ok, Pid}=amqp_subscriber:start_link(Msg), {ok, Req, State#state{ pid = Pid }} end. From cf8c1ef6bc90ee5fe13cac7e8689e78e8c0ed1dc Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 27 Nov 2013 13:02:36 +0800 Subject: [PATCH 306/405] #5107 unsubscribe --- src/bigwig_http_lager_stream.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 6051bb1..2efabc5 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -23,12 +23,14 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) -> + io:format("msg is ~p",[Msg]), case Msg of <<"kill">> -> Pid = State#state.pid, amqp_channel:close(Pid), {ok, Req, State}; Msg -> + io:format("subscriber start ~p",[Msg]), {ok, Pid}=amqp_subscriber:start_link(Msg), {ok, Req, State#state{ pid = Pid }} end. From df80579fef9f6f9e57cbbcadebeb491aeb15c5a4 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 27 Nov 2013 13:02:36 +0800 Subject: [PATCH 307/405] #5107 unsubscribe --- src/bigwig_http_lager_stream.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 6051bb1..2efabc5 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -23,12 +23,14 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) -> + io:format("msg is ~p",[Msg]), case Msg of <<"kill">> -> Pid = State#state.pid, amqp_channel:close(Pid), {ok, Req, State}; Msg -> + io:format("subscriber start ~p",[Msg]), {ok, Pid}=amqp_subscriber:start_link(Msg), {ok, Req, State#state{ pid = Pid }} end. From 7c352f460d59166cb0f2191b394777a502d6da8e Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 27 Nov 2013 15:55:58 +0800 Subject: [PATCH 308/405] #5107 unsubscribe --- priv/js/bigwig.js | 3 ++- src/bigwig_http_lager_stream.erl | 21 ++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 8ab8d24..362c9c6 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -126,6 +126,7 @@ function sendTxt(txt) { title: 'routingkey(' + txt + ')   ', close: function(event, ui) { closeSubscribe(); + window.location.reload(); }, buttons: { "Close": function() { $(this).dialog("close"); }, @@ -134,7 +135,7 @@ function sendTxt(txt) { }) }; function closeSubscribe(){ - websocket.send('kill'); + websocket.send('unsubscribe'); } $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 2efabc5..3f46566 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -6,7 +6,6 @@ -export([init/3, handle/2, terminate/3]). -export([websocket_init/3, websocket_handle/3, websocket_info/3, websocket_terminate/3]). --record(state, {pid}). init({tcp, http}, _Req, _Opts) -> {upgrade, protocol, cowboy_websocket}. @@ -22,18 +21,14 @@ websocket_init(_TransportName, Req, _Opts) -> {ok, Req, undefined_state}. %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too -websocket_handle({text, Msg}, Req, State) -> - io:format("msg is ~p",[Msg]), - case Msg of - <<"kill">> -> - Pid = State#state.pid, - amqp_channel:close(Pid), - {ok, Req, State}; - Msg -> - io:format("subscriber start ~p",[Msg]), - {ok, Pid}=amqp_subscriber:start_link(Msg), - {ok, Req, State#state{ pid = Pid }} - end. +websocket_handle({text, Msg}, Req, State) when Msg =:= <<"unsubscribe">> -> + io:format("msg is ~p",[Msg]), + amqp_subscriber:cast(unsubscribe), + {ok, Req, State}; +websocket_handle({text, Msg}, Req, State) when Msg =/= <<"unsubscribe">> -> + io:format("msg is ~p",[Msg]), + amqp_subscriber:start_link(Msg) of + {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), {reply, {text, Reply}, Req, State}; From 7dfa28a494c3ba4587d05f865e1e27c28613fb90 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 27 Nov 2013 15:55:58 +0800 Subject: [PATCH 309/405] #5107 unsubscribe --- priv/js/bigwig.js | 3 ++- src/bigwig_http_lager_stream.erl | 21 ++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 8ab8d24..362c9c6 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -126,6 +126,7 @@ function sendTxt(txt) { title: 'routingkey(' + txt + ')   ', close: function(event, ui) { closeSubscribe(); + window.location.reload(); }, buttons: { "Close": function() { $(this).dialog("close"); }, @@ -134,7 +135,7 @@ function sendTxt(txt) { }) }; function closeSubscribe(){ - websocket.send('kill'); + websocket.send('unsubscribe'); } $("body").delegate("a.tracer", "click", function(e){ var x1 = $(this).text().split("<<"); diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 2efabc5..3f46566 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -6,7 +6,6 @@ -export([init/3, handle/2, terminate/3]). -export([websocket_init/3, websocket_handle/3, websocket_info/3, websocket_terminate/3]). --record(state, {pid}). init({tcp, http}, _Req, _Opts) -> {upgrade, protocol, cowboy_websocket}. @@ -22,18 +21,14 @@ websocket_init(_TransportName, Req, _Opts) -> {ok, Req, undefined_state}. %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too -websocket_handle({text, Msg}, Req, State) -> - io:format("msg is ~p",[Msg]), - case Msg of - <<"kill">> -> - Pid = State#state.pid, - amqp_channel:close(Pid), - {ok, Req, State}; - Msg -> - io:format("subscriber start ~p",[Msg]), - {ok, Pid}=amqp_subscriber:start_link(Msg), - {ok, Req, State#state{ pid = Pid }} - end. +websocket_handle({text, Msg}, Req, State) when Msg =:= <<"unsubscribe">> -> + io:format("msg is ~p",[Msg]), + amqp_subscriber:cast(unsubscribe), + {ok, Req, State}; +websocket_handle({text, Msg}, Req, State) when Msg =/= <<"unsubscribe">> -> + io:format("msg is ~p",[Msg]), + amqp_subscriber:start_link(Msg) of + {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), {reply, {text, Reply}, Req, State}; From eb0b2381bf427709ac583544696e319617e12a55 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 27 Nov 2013 18:40:03 +0800 Subject: [PATCH 310/405] #5107 unsubscribe --- src/bigwig_http_lager_stream.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 3f46566..8567496 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -23,11 +23,12 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) when Msg =:= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), - amqp_subscriber:cast(unsubscribe), + Msg1=binary_to_atom(Msg,utf8), + amqp_subscriber:cast(Msg1), {ok, Req, State}; websocket_handle({text, Msg}, Req, State) when Msg =/= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), - amqp_subscriber:start_link(Msg) of + amqp_subscriber:start_link(Msg), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), From 77907bd2f927f1776b399162635e41aede2b58dc Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Wed, 27 Nov 2013 18:40:03 +0800 Subject: [PATCH 311/405] #5107 unsubscribe --- src/bigwig_http_lager_stream.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 3f46566..8567496 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -23,11 +23,12 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) when Msg =:= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), - amqp_subscriber:cast(unsubscribe), + Msg1=binary_to_atom(Msg,utf8), + amqp_subscriber:cast(Msg1), {ok, Req, State}; websocket_handle({text, Msg}, Req, State) when Msg =/= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), - amqp_subscriber:start_link(Msg) of + amqp_subscriber:start_link(Msg), {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), From 00a292a94d0f6c177241b687f31ee25d37197856 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 28 Nov 2013 15:41:20 +0800 Subject: [PATCH 312/405] #5107 unsubscribe --- priv/js/bigwig.js | 4 ++-- src/bigwig_http_lager_stream.erl | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 362c9c6..9433fb8 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -121,8 +121,8 @@ function sendTxt(txt) { .append('') ) .dialog({ - width: 550, - height: 650, + width: 750, + height: 500, title: 'routingkey(' + txt + ')   ', close: function(event, ui) { closeSubscribe(); diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 8567496..76013b0 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -25,10 +25,17 @@ websocket_handle({text, Msg}, Req, State) when Msg =:= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), Msg1=binary_to_atom(Msg,utf8), amqp_subscriber:cast(Msg1), + %% Pid = State, + %% erlang:exit(Pid,kill), {ok, Req, State}; websocket_handle({text, Msg}, Req, State) when Msg =/= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), amqp_subscriber:start_link(Msg), +%% {ok,Pid} -> +%% io:format("amqp_subscriber is ~p",[Pid]); +%% {error,_} -> +%% Pid = State +%% end, {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), From b9602df1f25fa0a301927534798011627fc9fc1d Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 28 Nov 2013 15:41:20 +0800 Subject: [PATCH 313/405] #5107 unsubscribe --- priv/js/bigwig.js | 4 ++-- src/bigwig_http_lager_stream.erl | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 362c9c6..9433fb8 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -121,8 +121,8 @@ function sendTxt(txt) { .append('') ) .dialog({ - width: 550, - height: 650, + width: 750, + height: 500, title: 'routingkey(' + txt + ')   ', close: function(event, ui) { closeSubscribe(); diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 8567496..76013b0 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -25,10 +25,17 @@ websocket_handle({text, Msg}, Req, State) when Msg =:= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), Msg1=binary_to_atom(Msg,utf8), amqp_subscriber:cast(Msg1), + %% Pid = State, + %% erlang:exit(Pid,kill), {ok, Req, State}; websocket_handle({text, Msg}, Req, State) when Msg =/= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), amqp_subscriber:start_link(Msg), +%% {ok,Pid} -> +%% io:format("amqp_subscriber is ~p",[Pid]); +%% {error,_} -> +%% Pid = State +%% end, {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), From b874e87b8df6bd01525e6ab3e2713ebf673c1a71 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 28 Nov 2013 17:31:24 +0800 Subject: [PATCH 314/405] #5107 unsubscribe --- src/bigwig_http_lager_stream.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 76013b0..95edaf6 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -23,19 +23,19 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) when Msg =:= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), - Msg1=binary_to_atom(Msg,utf8), - amqp_subscriber:cast(Msg1), + amqp_subscriber:cast(unsubscribe), %% Pid = State, %% erlang:exit(Pid,kill), {ok, Req, State}; websocket_handle({text, Msg}, Req, State) when Msg =/= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), - amqp_subscriber:start_link(Msg), -%% {ok,Pid} -> -%% io:format("amqp_subscriber is ~p",[Pid]); -%% {error,_} -> -%% Pid = State -%% end, + case amqp_subscriber:start_link(Msg) of + {ok,Pid} -> + io:format("amqp_subscriber init is ~p~n",[Pid]); + {error,_} -> + io:format("amqp_subscriber already start,subscribe ~n"), + amqp_subscriber:cast(subscribe) + end, {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), From 127ba004331980aef922939bb5f4dbf96d5691d6 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 28 Nov 2013 17:31:24 +0800 Subject: [PATCH 315/405] #5107 unsubscribe --- src/bigwig_http_lager_stream.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 76013b0..95edaf6 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -23,19 +23,19 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_handle({text, Msg}, Req, State) when Msg =:= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), - Msg1=binary_to_atom(Msg,utf8), - amqp_subscriber:cast(Msg1), + amqp_subscriber:cast(unsubscribe), %% Pid = State, %% erlang:exit(Pid,kill), {ok, Req, State}; websocket_handle({text, Msg}, Req, State) when Msg =/= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), - amqp_subscriber:start_link(Msg), -%% {ok,Pid} -> -%% io:format("amqp_subscriber is ~p",[Pid]); -%% {error,_} -> -%% Pid = State -%% end, + case amqp_subscriber:start_link(Msg) of + {ok,Pid} -> + io:format("amqp_subscriber init is ~p~n",[Pid]); + {error,_} -> + io:format("amqp_subscriber already start,subscribe ~n"), + amqp_subscriber:cast(subscribe) + end, {ok, Req, State}. websocket_info({bigwig, {bigwig_trace, Stats}}, Req, State) -> Reply = jsx:term_to_json([{lager, Stats}]), From 6dbd02c87e7a33a0a676037ab7c493fa07dfc476 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 29 Nov 2013 09:56:31 +0800 Subject: [PATCH 316/405] #5107 ajax --- priv/js/bigwig.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 9433fb8..ac077bf 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -32,17 +32,7 @@ $(function() { $('#dashboard').append($('

    Dashboard dump

    ')); $('#dashboard').append($('
    ' + JSON.stringify(json, null, 4) + '
    ')); }); - $.getJSON('/lager/status', function(json) { - $('#lager_status').append($('

    Lager Status

    ')); - for(var i=0; i' + json.ActiveTraces[i] + ''))[0].outerHTML; - } - var jsonText=JSON.stringify(json, null, 4); - var result=jsonText.replace(/\\"/g,"\""); - var result1=result.replace(/ /g,""); - $('#lager_status').append($('
    ' + result1 + '
    ')); - }); + uStatus(); $('#start_trace').bind('click', function(event) { var trace=$('#tracer').val(); var x = trace.split(":"); @@ -60,7 +50,7 @@ $(function() { type:'PUT', success: function(resp){ alert('Add tracer success'); - window.location.reload(); + uStatus(); } }) }); @@ -71,12 +61,26 @@ $(function() { type:'DELETE', success: function(resp){ alert('Clear all traces success'); - window.location.reload(); + uStatus(); } }) }); connect("/lager/stream"); }); +function uStatus() +{ + $.getJSON('/lager/status', function(json) { + $('#lager_status').html($('

    Lager Status

    ')); + for(var i=0; i' + json.ActiveTraces[i] + ''))[0].outerHTML; + } + var jsonText=JSON.stringify(json, null, 4); + var result=jsonText.replace(/\\"/g,"\""); + var result1=result.replace(/ /g,""); + $('#lager_status').append($('
    ' + result1 + '
    ')); + }); +}; function connect(to) { var host = document.location.host; From 27c01f5cfdedf9a915a236b7e06578761d3c1d74 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 29 Nov 2013 09:56:31 +0800 Subject: [PATCH 317/405] #5107 ajax --- priv/js/bigwig.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 9433fb8..ac077bf 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -32,17 +32,7 @@ $(function() { $('#dashboard').append($('

    Dashboard dump

    ')); $('#dashboard').append($('
    ' + JSON.stringify(json, null, 4) + '
    ')); }); - $.getJSON('/lager/status', function(json) { - $('#lager_status').append($('

    Lager Status

    ')); - for(var i=0; i' + json.ActiveTraces[i] + ''))[0].outerHTML; - } - var jsonText=JSON.stringify(json, null, 4); - var result=jsonText.replace(/\\"/g,"\""); - var result1=result.replace(/ /g,""); - $('#lager_status').append($('
    ' + result1 + '
    ')); - }); + uStatus(); $('#start_trace').bind('click', function(event) { var trace=$('#tracer').val(); var x = trace.split(":"); @@ -60,7 +50,7 @@ $(function() { type:'PUT', success: function(resp){ alert('Add tracer success'); - window.location.reload(); + uStatus(); } }) }); @@ -71,12 +61,26 @@ $(function() { type:'DELETE', success: function(resp){ alert('Clear all traces success'); - window.location.reload(); + uStatus(); } }) }); connect("/lager/stream"); }); +function uStatus() +{ + $.getJSON('/lager/status', function(json) { + $('#lager_status').html($('

    Lager Status

    ')); + for(var i=0; i' + json.ActiveTraces[i] + ''))[0].outerHTML; + } + var jsonText=JSON.stringify(json, null, 4); + var result=jsonText.replace(/\\"/g,"\""); + var result1=result.replace(/ /g,""); + $('#lager_status').append($('
    ' + result1 + '
    ')); + }); +}; function connect(to) { var host = document.location.host; From 4fcfc9613cef847a1baff81f18a5da60cb194954 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 29 Nov 2013 10:24:20 +0800 Subject: [PATCH 318/405] #5107 ajax --- priv/js/bigwig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index ac077bf..c6ac559 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -130,7 +130,7 @@ function sendTxt(txt) { title: 'routingkey(' + txt + ')   ', close: function(event, ui) { closeSubscribe(); - window.location.reload(); + uStatus(); }, buttons: { "Close": function() { $(this).dialog("close"); }, From cbc702d51775e692b07575357b9cb8f35af20d47 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 29 Nov 2013 10:24:20 +0800 Subject: [PATCH 319/405] #5107 ajax --- priv/js/bigwig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index ac077bf..c6ac559 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -130,7 +130,7 @@ function sendTxt(txt) { title: 'routingkey(' + txt + ')   ', close: function(event, ui) { closeSubscribe(); - window.location.reload(); + uStatus(); }, buttons: { "Close": function() { $(this).dialog("close"); }, From b198bc8cda12b7a69780e188bfcd654c45739573 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 29 Nov 2013 18:01:40 +0800 Subject: [PATCH 320/405] #5107 unsubscirbe --- priv/js/bigwig.js | 69 ++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index c6ac559..29f8321 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -65,6 +65,40 @@ $(function() { } }) }); + var $dialog= $('
    ') + .dialog({ + autoOpen: false, + width: 750, + height: 500, + title: 'routingkey(' + txt + ')   ', + close: function(event, ui) { + closeSubscribe(); + uStatus(); + }, + buttons: { + "Close": function() { $(this).dialog("close"); }, + "Stop Trace": function() {} + }, + open: function() { + $('#trace_dialog').append( $('
    ') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ) + } + }); + $('#a.tracer').click(function() { + var x1 = $(this).text().split("<<"); + var x2 =x1[1].split(">>"); + var x3 = x2[0].replace(/\"/g, ""); + websocket.send(x3); + $dialog.dialog('open'); + e.preventDefault(); + }); connect("/lager/stream"); }); function uStatus() @@ -112,39 +146,6 @@ function onMessage(evt) { .append('') ); }; -function sendTxt(txt) { - websocket.send(txt); - $('
    ') - .append( $('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - ) - .dialog({ - width: 750, - height: 500, - title: 'routingkey(' + txt + ')   ', - close: function(event, ui) { - closeSubscribe(); - uStatus(); - }, - buttons: { - "Close": function() { $(this).dialog("close"); }, - "Stop Trace": function() {} - } - }) -}; function closeSubscribe(){ websocket.send('unsubscribe'); -} -$("body").delegate("a.tracer", "click", function(e){ - var x1 = $(this).text().split("<<"); - var x2 =x1[1].split(">>"); - var x3 = x2[0].replace(/\"/g, ""); - sendTxt(x3); - e.preventDefault(); -}); \ No newline at end of file +}; \ No newline at end of file From 7ae6610abcb494a72c8000f1f181c036e73b556d Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 29 Nov 2013 18:01:40 +0800 Subject: [PATCH 321/405] #5107 unsubscirbe --- priv/js/bigwig.js | 69 ++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index c6ac559..29f8321 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -65,6 +65,40 @@ $(function() { } }) }); + var $dialog= $('
    ') + .dialog({ + autoOpen: false, + width: 750, + height: 500, + title: 'routingkey(' + txt + ')   ', + close: function(event, ui) { + closeSubscribe(); + uStatus(); + }, + buttons: { + "Close": function() { $(this).dialog("close"); }, + "Stop Trace": function() {} + }, + open: function() { + $('#trace_dialog').append( $('
    ') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ) + } + }); + $('#a.tracer').click(function() { + var x1 = $(this).text().split("<<"); + var x2 =x1[1].split(">>"); + var x3 = x2[0].replace(/\"/g, ""); + websocket.send(x3); + $dialog.dialog('open'); + e.preventDefault(); + }); connect("/lager/stream"); }); function uStatus() @@ -112,39 +146,6 @@ function onMessage(evt) { .append('') ); }; -function sendTxt(txt) { - websocket.send(txt); - $('
    ') - .append( $('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - ) - .dialog({ - width: 750, - height: 500, - title: 'routingkey(' + txt + ')   ', - close: function(event, ui) { - closeSubscribe(); - uStatus(); - }, - buttons: { - "Close": function() { $(this).dialog("close"); }, - "Stop Trace": function() {} - } - }) -}; function closeSubscribe(){ websocket.send('unsubscribe'); -} -$("body").delegate("a.tracer", "click", function(e){ - var x1 = $(this).text().split("<<"); - var x2 =x1[1].split(">>"); - var x3 = x2[0].replace(/\"/g, ""); - sendTxt(x3); - e.preventDefault(); -}); \ No newline at end of file +}; \ No newline at end of file From 43356a2a147629803fa667b5dae2627bfcc23aeb Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 29 Nov 2013 18:23:31 +0800 Subject: [PATCH 322/405] #5107 unsubscribe --- priv/js/bigwig.js | 72 ++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 29f8321..689eb64 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -65,40 +65,6 @@ $(function() { } }) }); - var $dialog= $('
    ') - .dialog({ - autoOpen: false, - width: 750, - height: 500, - title: 'routingkey(' + txt + ')   ', - close: function(event, ui) { - closeSubscribe(); - uStatus(); - }, - buttons: { - "Close": function() { $(this).dialog("close"); }, - "Stop Trace": function() {} - }, - open: function() { - $('#trace_dialog').append( $('
    ') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - ) - } - }); - $('#a.tracer').click(function() { - var x1 = $(this).text().split("<<"); - var x2 =x1[1].split(">>"); - var x3 = x2[0].replace(/\"/g, ""); - websocket.send(x3); - $dialog.dialog('open'); - e.preventDefault(); - }); connect("/lager/stream"); }); function uStatus() @@ -146,6 +112,42 @@ function onMessage(evt) { .append('') ); }; +function sendTxt(txt) { + websocket.send(txt); + $('
    ') + .dialog({ + width: 750, + height: 500, + title: 'routingkey(' + txt + ')   ', + close: function(event, ui) { + closeSubscribe(); + uStatus(); + window.location.reload(); + }, + buttons: { + "Close": function() { $(this).dialog("close"); }, + "Stop Trace": function() {} + }, + open: function() { + $('#trace_dialog').append( $('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ) + } + }) +}; function closeSubscribe(){ websocket.send('unsubscribe'); -}; \ No newline at end of file +}; +$("body").delegate("a.tracer", "click", function(e){ + var x1 = $(this).text().split("<<"); + var x2 =x1[1].split(">>"); + var x3 = x2[0].replace(/\"/g, ""); + sendTxt(x3); + e.preventDefault(); +}); \ No newline at end of file From 4fb0dfb0e941b0a3bc718b8c75c4041ab42af9f0 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Fri, 29 Nov 2013 18:23:31 +0800 Subject: [PATCH 323/405] #5107 unsubscribe --- priv/js/bigwig.js | 72 ++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 29f8321..689eb64 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -65,40 +65,6 @@ $(function() { } }) }); - var $dialog= $('
    ') - .dialog({ - autoOpen: false, - width: 750, - height: 500, - title: 'routingkey(' + txt + ')   ', - close: function(event, ui) { - closeSubscribe(); - uStatus(); - }, - buttons: { - "Close": function() { $(this).dialog("close"); }, - "Stop Trace": function() {} - }, - open: function() { - $('#trace_dialog').append( $('
    ') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - ) - } - }); - $('#a.tracer').click(function() { - var x1 = $(this).text().split("<<"); - var x2 =x1[1].split(">>"); - var x3 = x2[0].replace(/\"/g, ""); - websocket.send(x3); - $dialog.dialog('open'); - e.preventDefault(); - }); connect("/lager/stream"); }); function uStatus() @@ -146,6 +112,42 @@ function onMessage(evt) { .append('') ); }; +function sendTxt(txt) { + websocket.send(txt); + $('
    ') + .dialog({ + width: 750, + height: 500, + title: 'routingkey(' + txt + ')   ', + close: function(event, ui) { + closeSubscribe(); + uStatus(); + window.location.reload(); + }, + buttons: { + "Close": function() { $(this).dialog("close"); }, + "Stop Trace": function() {} + }, + open: function() { + $('#trace_dialog').append( $('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ) + } + }) +}; function closeSubscribe(){ websocket.send('unsubscribe'); -}; \ No newline at end of file +}; +$("body").delegate("a.tracer", "click", function(e){ + var x1 = $(this).text().split("<<"); + var x2 =x1[1].split(">>"); + var x3 = x2[0].replace(/\"/g, ""); + sendTxt(x3); + e.preventDefault(); +}); \ No newline at end of file From d0617b29837e3ccd7df2fecc8722d92b4182b4e9 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Sat, 30 Nov 2013 15:26:38 +0800 Subject: [PATCH 324/405] #5107 modify the dialog format --- priv/js/bigwig.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 689eb64..300c97d 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -115,30 +115,27 @@ function onMessage(evt) { function sendTxt(txt) { websocket.send(txt); $('
    ') + .append( $('
    ') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ) .dialog({ width: 750, height: 500, title: 'routingkey(' + txt + ')   ', close: function(event, ui) { closeSubscribe(); - uStatus(); - window.location.reload(); + $(this).remove(); }, buttons: { "Close": function() { $(this).dialog("close"); }, "Stop Trace": function() {} }, - open: function() { - $('#trace_dialog').append( $('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - ) - } }) }; function closeSubscribe(){ From 5661338cd87193990441b308df58adfc50bdbafa Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Sat, 30 Nov 2013 15:26:38 +0800 Subject: [PATCH 325/405] #5107 modify the dialog format --- priv/js/bigwig.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 689eb64..300c97d 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -115,30 +115,27 @@ function onMessage(evt) { function sendTxt(txt) { websocket.send(txt); $('
    ') + .append( $('
    ') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + .append('') + ) .dialog({ width: 750, height: 500, title: 'routingkey(' + txt + ')   ', close: function(event, ui) { closeSubscribe(); - uStatus(); - window.location.reload(); + $(this).remove(); }, buttons: { "Close": function() { $(this).dialog("close"); }, "Stop Trace": function() {} }, - open: function() { - $('#trace_dialog').append( $('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - .append('') - ) - } }) }; function closeSubscribe(){ From 8757a6171316f285c25f49028f55c38c11321db9 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Sat, 30 Nov 2013 17:21:15 +0800 Subject: [PATCH 326/405] #5107 fix unsubcribe prolem --- priv/js/bigwig.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 300c97d..60ba565 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -130,7 +130,8 @@ function sendTxt(txt) { title: 'routingkey(' + txt + ')   ', close: function(event, ui) { closeSubscribe(); - $(this).remove(); + $(this).remove(); + window.location.reload(); }, buttons: { "Close": function() { $(this).dialog("close"); }, From 4baa5c32e30c1941f7dd5b706399c9f572475b10 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Sat, 30 Nov 2013 17:21:15 +0800 Subject: [PATCH 327/405] #5107 fix unsubcribe prolem --- priv/js/bigwig.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 300c97d..60ba565 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -130,7 +130,8 @@ function sendTxt(txt) { title: 'routingkey(' + txt + ')   ', close: function(event, ui) { closeSubscribe(); - $(this).remove(); + $(this).remove(); + window.location.reload(); }, buttons: { "Close": function() { $(this).dialog("close"); }, From 289cf4e1858f19ef2afd2e84bfa28d0ee707c3c9 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 2 Dec 2013 14:19:11 +0800 Subject: [PATCH 328/405] #5107 stop single trace --- priv/html/index.html | 1 + priv/js/bigwig.js | 23 ++++++++++++++++++++++- src/bigwig_http_lager.erl | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index 220af14..a535eb4 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -138,6 +138,7 @@

    BigWig

    Tracer: +
    diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 60ba565..d839a5b 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -54,6 +54,27 @@ $(function() { } }) }); + $('#del_trace').bind('click', function(event) { + var trace=$('#tracer').val(); + var x = trace.split(":"); + var tracer = ''; + for(var i=0; i< x.length; ++i) + { + tracer = tracer + x[i] + ','; + } + tracer = tracer.substring(0,tracer.length-1); + + var url = '/lager/tracer/'+tracer; + + $.ajax({ + url:url, + type:'DELETE', + success: function(resp){ + alert('Stop tracer success'); + uStatus(); + } + }) + }); $('#clear_all_traces').bind('click', function(event) { var url = '/lager/tracer/all'; $.ajax({ @@ -103,7 +124,7 @@ function onMessage(evt) { x2 = x2+ x1[i]; } $('#trace_dialog').append( $('
    ') - .append('') + .append('') .append('') .append('') .append('') diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 77c45d0..e57a6d5 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -23,8 +23,8 @@ handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> handle_add_tracer(Tracer, Req, State); handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, <<"all">>], Req, State) -> handle_clear_traces(Req, State); -handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, _Tracer], Req, State) -> - not_found(Req, State); +handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> + handle_del_trace(Tracer, Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). @@ -62,6 +62,38 @@ handle_add_tracer(Tracer, Req, State) -> _ -> ok end, {ok,Req,State}. +handle_del_trace(Tracer, Req, State) -> + io:format("tracer is ~p", [Tracer]), + All = binary:split(Tracer,[<<",">>],[global]), + + amqp_tracer:start_link(), + case length(All) of + 5 -> + Attr1 = binary_to_atom(lists:nth(1, All), utf8), + RoutingKey = lists:nth(2, All), + Filter = [{binary_to_atom(lists:nth(3, All), utf8), binary_to_integer(lists:nth(4, All))}], + Level = binary_to_atom(lists:nth(5, All), utf8), + Trace0 = { Filter, Level, {lager_amqp_backend, RoutingKey} }, + case lager_util:validate_trace(Trace0) of + {ok, Trace} -> + amqp_tracer:stop_trace(Attr1, Trace); + Error -> + Error + end; + 4 -> + RoutingKey = lists:nth(1, All), + Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + Level = binary_to_atom(lists:nth(4, All), utf8), + Trace0 = { Filter, Level, {lager_amqp_backend, RoutingKey} }, + case lager_util:validate_trace(Trace0) of + {ok, Trace} -> + amqp_tracer:stop_trace(Trace); + Error -> + Error + end; + _ -> ok + end, + {ok,Req,State}. handle_clear_traces(Req, State) -> lager:clear_all_traces(), {ok,Req,State}. From bcd29ed98152c0b25e9812e82a8f9c8ba4722f9e Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Mon, 2 Dec 2013 14:19:11 +0800 Subject: [PATCH 329/405] #5107 stop single trace --- priv/html/index.html | 1 + priv/js/bigwig.js | 23 ++++++++++++++++++++++- src/bigwig_http_lager.erl | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index 220af14..a535eb4 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -138,6 +138,7 @@

    BigWig

    Tracer: +
    diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index 60ba565..d839a5b 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -54,6 +54,27 @@ $(function() { } }) }); + $('#del_trace').bind('click', function(event) { + var trace=$('#tracer').val(); + var x = trace.split(":"); + var tracer = ''; + for(var i=0; i< x.length; ++i) + { + tracer = tracer + x[i] + ','; + } + tracer = tracer.substring(0,tracer.length-1); + + var url = '/lager/tracer/'+tracer; + + $.ajax({ + url:url, + type:'DELETE', + success: function(resp){ + alert('Stop tracer success'); + uStatus(); + } + }) + }); $('#clear_all_traces').bind('click', function(event) { var url = '/lager/tracer/all'; $.ajax({ @@ -103,7 +124,7 @@ function onMessage(evt) { x2 = x2+ x1[i]; } $('#trace_dialog').append( $('
    ') - .append('') + .append('') .append('') .append('') .append('') diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index 77c45d0..e57a6d5 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -23,8 +23,8 @@ handle_path(<<"PUT">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> handle_add_tracer(Tracer, Req, State); handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, <<"all">>], Req, State) -> handle_clear_traces(Req, State); -handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, _Tracer], Req, State) -> - not_found(Req, State); +handle_path(<<"DELETE">>, [<<"lager">>, <<"tracer">>, Tracer], Req, State) -> + handle_del_trace(Tracer, Req, State); handle_path(_, _, Req, State) -> not_found(Req, State). @@ -62,6 +62,38 @@ handle_add_tracer(Tracer, Req, State) -> _ -> ok end, {ok,Req,State}. +handle_del_trace(Tracer, Req, State) -> + io:format("tracer is ~p", [Tracer]), + All = binary:split(Tracer,[<<",">>],[global]), + + amqp_tracer:start_link(), + case length(All) of + 5 -> + Attr1 = binary_to_atom(lists:nth(1, All), utf8), + RoutingKey = lists:nth(2, All), + Filter = [{binary_to_atom(lists:nth(3, All), utf8), binary_to_integer(lists:nth(4, All))}], + Level = binary_to_atom(lists:nth(5, All), utf8), + Trace0 = { Filter, Level, {lager_amqp_backend, RoutingKey} }, + case lager_util:validate_trace(Trace0) of + {ok, Trace} -> + amqp_tracer:stop_trace(Attr1, Trace); + Error -> + Error + end; + 4 -> + RoutingKey = lists:nth(1, All), + Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + Level = binary_to_atom(lists:nth(4, All), utf8), + Trace0 = { Filter, Level, {lager_amqp_backend, RoutingKey} }, + case lager_util:validate_trace(Trace0) of + {ok, Trace} -> + amqp_tracer:stop_trace(Trace); + Error -> + Error + end; + _ -> ok + end, + {ok,Req,State}. handle_clear_traces(Req, State) -> lager:clear_all_traces(), {ok,Req,State}. From 705b6bfe3449ab56a7b2be3b40f2ac9ab95ddbaf Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 3 Dec 2013 15:02:12 +0800 Subject: [PATCH 330/405] jsx --- rebar.config | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 69b67c0..feef7d5 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,6 @@ {deps, [ {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, - {jsx, ".*", {git, "https://github.com/yuzhaoren/jsx.git", {branch, "beamspirit"}}}, {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}} - %%{jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} + {jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} ]}. From e475729b383f74ea6c6cca4a1c72b7d90d66e5f3 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 3 Dec 2013 15:05:58 +0800 Subject: [PATCH 331/405] jsx --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index feef7d5..c690ad5 100644 --- a/rebar.config +++ b/rebar.config @@ -7,6 +7,6 @@ {deps, [ {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, - {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}} + {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}}, {jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} ]}. From 64db979d4f2a06c74a162a48b37f917cf7fccfa1 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Tue, 3 Dec 2013 15:23:02 +0800 Subject: [PATCH 332/405] add jiffy_ext module to handle term to json --- src/{jsx_ext.erl => jiffy_ext.erl} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/{jsx_ext.erl => jiffy_ext.erl} (97%) diff --git a/src/jsx_ext.erl b/src/jiffy_ext.erl similarity index 97% rename from src/jsx_ext.erl rename to src/jiffy_ext.erl index 2d875a2..bffedff 100644 --- a/src/jsx_ext.erl +++ b/src/jiffy_ext.erl @@ -6,7 +6,7 @@ %%% @end %%% Created : 18 Nov 2013 by Jack Tang %%%------------------------------------------------------------------- --module(jsx_ext). +-module(jiffy_ext). %% API -export([]). From 825841aedc61d01667b6afd371a45af27c68135f Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Tue, 3 Dec 2013 15:23:02 +0800 Subject: [PATCH 333/405] add jiffy_ext module to handle term to json --- src/{jsx_ext.erl => jiffy_ext.erl} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/{jsx_ext.erl => jiffy_ext.erl} (97%) diff --git a/src/jsx_ext.erl b/src/jiffy_ext.erl similarity index 97% rename from src/jsx_ext.erl rename to src/jiffy_ext.erl index 2d875a2..bffedff 100644 --- a/src/jsx_ext.erl +++ b/src/jiffy_ext.erl @@ -6,7 +6,7 @@ %%% @end %%% Created : 18 Nov 2013 by Jack Tang %%%------------------------------------------------------------------- --module(jsx_ext). +-module(jiffy_ext). %% API -export([]). From f818152daee4107cd15a3e81f82fce7e5256bb8d Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Tue, 3 Dec 2013 15:24:56 +0800 Subject: [PATCH 334/405] add jiffy_ext module to handle term to json --- rebar.config | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index c690ad5..295ad12 100644 --- a/rebar.config +++ b/rebar.config @@ -7,6 +7,8 @@ {deps, [ {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, - {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}}, - {jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} + {jiffy, ".*", {git, "git://github.com/davisp/jiffy", "master"}}, + {jsx, ".*", {git, "https://github.com/yuzhaoren/jsx.git", {branch, "beamspirit"}}}, + {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}} + %%{jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} ]}. From 974e5d9a6bd5802d54d4a641a5683f351ab67c00 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 3 Dec 2013 15:40:19 +0800 Subject: [PATCH 335/405] #5107 add entop --- priv/html/index.html | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index a535eb4..9018959 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -112,6 +112,16 @@

    BigWig

    + + + +
    Accumulate Pause @@ -133,7 +143,6 @@

    BigWig

    '+"Time"+' '+"Node"+'
    '+"Time"+' '+"Node"+'
    '+"Time"+' '+"Node"+'
    '+"Time"+' '+"Node"+'
    '+x1[0]+x1[1]+' '+x1[2]+' '+x1[3]+' '+x2+'
    '+x1[0]+x1[1]+' '+x1[2]+' '+x1[3]+' '+x2+'
    '+x1[0]+x1[1]+' '+x1[2]+' '+x1[3]+' '+x2+'
    '+x1[0]+x1[1]+' '+x1[2]+' '+x1[3]+' '+x2+''+"Message"+''+"Message"+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+''+x2+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+''+x2+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+''+x2+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+''+x2+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+'
    '+"Time"+' '+"Node"+' '+"Level"+' '+"Message"+'
    '+x1[0]+x1[1]+''+x1[0]+','+x1[1]+' '+x1[2]+'
    '+x1[0]+x1[1]+''+x1[0]+','+x1[1]+' '+x1[2]+'
    -
    Tracer: From 5fa17b9881757a3d48f8a8c1e88f50ab86e23443 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 3 Dec 2013 15:40:19 +0800 Subject: [PATCH 336/405] #5107 add entop --- priv/html/index.html | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index a535eb4..9018959 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -112,6 +112,16 @@

    BigWig

    + + + + Accumulate Pause @@ -133,7 +143,6 @@

    BigWig

    -
    Tracer: From 399f7934f1eb489083c5f8922366636caa5301b4 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 3 Dec 2013 18:35:18 +0800 Subject: [PATCH 337/405] #5151 add jst_ext encode --- src/jiffy_ext.erl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index bffedff..2679b6d 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -9,11 +9,23 @@ -module(jiffy_ext). %% API --export([]). +-export([encode/1]). %%%=================================================================== %%% API %%%=================================================================== +encode(Binary) when is_binary(Binary) -> + jiffy_ext:encode(Binary); +encode(Atom) when is_atom(Atom) -> + jiffy:encode(Atom); +encode(Pid) when is_pid(Pid) -> + jiffy:encode(list_to_binary(erlang:pid_to_list(Pid))); +encode(Port) when is_port(Port) -> + jiffy:encode(list_to_binary(erlang:port_to_list(Port))); +encode(Ref) when is_reference(Ref) -> + jiffy:encode(list_to_binary(erlang:ref_to_list(Ref))); +encode({{Y,M,D},{Hrs,Mins,Secs}}) -> + jiffy_ext:encode([Y,M,D,Hrs,Mins,Secs]). %%-------------------------------------------------------------------- %% @doc From aef4bd15d32a362c3e45546b6462d789267f99da Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Tue, 3 Dec 2013 18:35:18 +0800 Subject: [PATCH 338/405] #5151 add jst_ext encode --- src/jiffy_ext.erl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index bffedff..2679b6d 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -9,11 +9,23 @@ -module(jiffy_ext). %% API --export([]). +-export([encode/1]). %%%=================================================================== %%% API %%%=================================================================== +encode(Binary) when is_binary(Binary) -> + jiffy_ext:encode(Binary); +encode(Atom) when is_atom(Atom) -> + jiffy:encode(Atom); +encode(Pid) when is_pid(Pid) -> + jiffy:encode(list_to_binary(erlang:pid_to_list(Pid))); +encode(Port) when is_port(Port) -> + jiffy:encode(list_to_binary(erlang:port_to_list(Port))); +encode(Ref) when is_reference(Ref) -> + jiffy:encode(list_to_binary(erlang:ref_to_list(Ref))); +encode({{Y,M,D},{Hrs,Mins,Secs}}) -> + jiffy_ext:encode([Y,M,D,Hrs,Mins,Secs]). %%-------------------------------------------------------------------- %% @doc From 1c607d19573dd0af6b8ff7ff4b1eab645d8613fa Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 5 Dec 2013 10:27:00 +0800 Subject: [PATCH 339/405] #5151 --- src/jiffy_ext.erl | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index 2679b6d..eaa98fe 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -9,30 +9,44 @@ -module(jiffy_ext). %% API --export([encode/1]). +-export([encode/1, + to_jiffy/1]). + %%%=================================================================== %%% API %%%=================================================================== -encode(Binary) when is_binary(Binary) -> - jiffy_ext:encode(Binary); -encode(Atom) when is_atom(Atom) -> - jiffy:encode(Atom); -encode(Pid) when is_pid(Pid) -> - jiffy:encode(list_to_binary(erlang:pid_to_list(Pid))); -encode(Port) when is_port(Port) -> - jiffy:encode(list_to_binary(erlang:port_to_list(Port))); -encode(Ref) when is_reference(Ref) -> - jiffy:encode(list_to_binary(erlang:ref_to_list(Ref))); -encode({{Y,M,D},{Hrs,Mins,Secs}}) -> - jiffy_ext:encode([Y,M,D,Hrs,Mins,Secs]). - +encode(Data) -> + Jiffy=to_jiffy(Data), + jiffy:encode(Jiffy). %%-------------------------------------------------------------------- %% @doc %% @spec %% @end %%-------------------------------------------------------------------- - %%%=================================================================== %%% Internal functions %%%=================================================================== +to_jiffy(Tuple) when is_tuple(Tuple) -> + to_jiffy_list(tuple_to_list(Tuple),[]); +to_jiffy(Port) when is_port(Port) -> + list_to_binary(erlang:port_to_list(Port)); +to_jiffy(Ref) when is_reference(Ref) -> + list_to_binary(erlang:ref_to_list(Ref)); +to_jiffy(Pid) when is_pid(Pid) -> + list_to_binary(erlang:pid_to_list(Pid)); +to_jiffy(Values) when is_list(Values) -> + to_jiffy_list(Values, []); +to_jiffy(Val) -> + Val. + + +to_jiffy_list([], Acc) -> + lists:reverse(Acc); +to_jiffy_list([Val | Rest], Acc0) -> + Acc = [to_jiffy(Val) | Acc0], + to_jiffy_list(Rest, Acc). + + + + From d83b148b3064a032b9babc790f0398ae5a9381bf Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 5 Dec 2013 10:27:00 +0800 Subject: [PATCH 340/405] #5151 --- src/jiffy_ext.erl | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index 2679b6d..eaa98fe 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -9,30 +9,44 @@ -module(jiffy_ext). %% API --export([encode/1]). +-export([encode/1, + to_jiffy/1]). + %%%=================================================================== %%% API %%%=================================================================== -encode(Binary) when is_binary(Binary) -> - jiffy_ext:encode(Binary); -encode(Atom) when is_atom(Atom) -> - jiffy:encode(Atom); -encode(Pid) when is_pid(Pid) -> - jiffy:encode(list_to_binary(erlang:pid_to_list(Pid))); -encode(Port) when is_port(Port) -> - jiffy:encode(list_to_binary(erlang:port_to_list(Port))); -encode(Ref) when is_reference(Ref) -> - jiffy:encode(list_to_binary(erlang:ref_to_list(Ref))); -encode({{Y,M,D},{Hrs,Mins,Secs}}) -> - jiffy_ext:encode([Y,M,D,Hrs,Mins,Secs]). - +encode(Data) -> + Jiffy=to_jiffy(Data), + jiffy:encode(Jiffy). %%-------------------------------------------------------------------- %% @doc %% @spec %% @end %%-------------------------------------------------------------------- - %%%=================================================================== %%% Internal functions %%%=================================================================== +to_jiffy(Tuple) when is_tuple(Tuple) -> + to_jiffy_list(tuple_to_list(Tuple),[]); +to_jiffy(Port) when is_port(Port) -> + list_to_binary(erlang:port_to_list(Port)); +to_jiffy(Ref) when is_reference(Ref) -> + list_to_binary(erlang:ref_to_list(Ref)); +to_jiffy(Pid) when is_pid(Pid) -> + list_to_binary(erlang:pid_to_list(Pid)); +to_jiffy(Values) when is_list(Values) -> + to_jiffy_list(Values, []); +to_jiffy(Val) -> + Val. + + +to_jiffy_list([], Acc) -> + lists:reverse(Acc); +to_jiffy_list([Val | Rest], Acc0) -> + Acc = [to_jiffy(Val) | Acc0], + to_jiffy_list(Rest, Acc). + + + + From 606567650cee918b1e4dcbe9e1669d648043ead5 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 5 Dec 2013 16:19:45 +0800 Subject: [PATCH 341/405] jiffy_ext --- src/bigwig.erl | 2 +- src/bigwig_http_module.erl | 2 +- src/bigwig_http_pid.erl | 2 +- src/bigwig_http_vm.erl | 2 +- src/jiffy_ext.erl | 26 +++++++++++++++++++------- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/bigwig.erl b/src/bigwig.erl index 7f8cda4..6fdee28 100644 --- a/src/bigwig.erl +++ b/src/bigwig.erl @@ -9,7 +9,7 @@ start() -> application:ensure_started(sasl), application:ensure_started(cowlib), application:ensure_started(cowboy), - %application:ensure_started(lager), + application:ensure_started(lager), application:start(bigwig). stop() -> diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index 9c25838..8780bc9 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -97,7 +97,7 @@ to_module_info(Bin) -> json_response(Info, Req, State) -> - Body = jsx:term_to_json(Info), + Body = jiffy_ext:encode(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index 81ee80b..f20485d 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -91,7 +91,7 @@ pid_response(Pid, Req, State) -> Info -> % Info1=lists:map(fun(T) -> to_json(T) end,Info), % Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), - Body = jsx:term_to_json(Info), + Body = jiffy_ext:encode(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} diff --git a/src/bigwig_http_vm.erl b/src/bigwig_http_vm.erl index d11b8a8..87fd0ce 100644 --- a/src/bigwig_http_vm.erl +++ b/src/bigwig_http_vm.erl @@ -13,7 +13,7 @@ init({tcp, http}, Req, _Opts) -> handle(Req, State) -> io:format("===> vm ~p", [all()]), - Body = jsx:term_to_json(all()), + Body = jiffy_ext:encode(all()), io:format("===> vm ~p", [Body]), Headers = [{<<"Content-Type">>, <<"application/json">>}], diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index eaa98fe..a3143ef 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -9,15 +9,16 @@ -module(jiffy_ext). %% API --export([encode/1, - to_jiffy/1]). +-export([encode/1]). %%%=================================================================== %%% API %%%=================================================================== encode(Data) -> + Jiffy=to_jiffy(Data), + io:format("Jiffy is ~p",[Jiffy]), jiffy:encode(Jiffy). %%-------------------------------------------------------------------- %% @doc @@ -27,6 +28,8 @@ encode(Data) -> %%%=================================================================== %%% Internal functions %%%=================================================================== +to_jiffy([First|_]=List) when is_tuple(First), size(First) =:= 2 -> + to_jiffy_props(List,[]); to_jiffy(Tuple) when is_tuple(Tuple) -> to_jiffy_list(tuple_to_list(Tuple),[]); to_jiffy(Port) when is_port(Port) -> @@ -40,13 +43,22 @@ to_jiffy(Values) when is_list(Values) -> to_jiffy(Val) -> Val. +to_jiffy_props([{Key, Value}|Rest], Acc) -> + K = to_jiffy(Key), + V = to_jiffy(Value), + to_jiffy_props(Rest, [{K, V} | Acc]); -to_jiffy_list([], Acc) -> - lists:reverse(Acc); -to_jiffy_list([Val | Rest], Acc0) -> - Acc = [to_jiffy(Val) | Acc0], - to_jiffy_list(Rest, Acc). +to_jiffy_props([Other|Rest], Acc) -> + + to_jiffy_props(Rest,[to_jiffy(Other)|Acc]); +to_jiffy_props([], Acc) -> + {lists:reverse(Acc)}. +to_jiffy_list([], Acc) -> + lists:reverse(Acc); +to_jiffy_list([Val | Rest], Acc0) -> + Acc = [to_jiffy(Val) | Acc0], + to_jiffy_list(Rest, Acc). \ No newline at end of file From 81a7873bf39890173433fd767154b9b2e25f3062 Mon Sep 17 00:00:00 2001 From: Zhaoren Yu Date: Thu, 5 Dec 2013 16:19:45 +0800 Subject: [PATCH 342/405] jiffy_ext --- src/bigwig.erl | 2 +- src/bigwig_http_module.erl | 2 +- src/bigwig_http_pid.erl | 2 +- src/bigwig_http_vm.erl | 2 +- src/jiffy_ext.erl | 26 +++++++++++++++++++------- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/bigwig.erl b/src/bigwig.erl index 7f8cda4..6fdee28 100644 --- a/src/bigwig.erl +++ b/src/bigwig.erl @@ -9,7 +9,7 @@ start() -> application:ensure_started(sasl), application:ensure_started(cowlib), application:ensure_started(cowboy), - %application:ensure_started(lager), + application:ensure_started(lager), application:start(bigwig). stop() -> diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index 9c25838..8780bc9 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -97,7 +97,7 @@ to_module_info(Bin) -> json_response(Info, Req, State) -> - Body = jsx:term_to_json(Info), + Body = jiffy_ext:encode(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index 81ee80b..f20485d 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -91,7 +91,7 @@ pid_response(Pid, Req, State) -> Info -> % Info1=lists:map(fun(T) -> to_json(T) end,Info), % Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), - Body = jsx:term_to_json(Info), + Body = jiffy_ext:encode(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} diff --git a/src/bigwig_http_vm.erl b/src/bigwig_http_vm.erl index d11b8a8..87fd0ce 100644 --- a/src/bigwig_http_vm.erl +++ b/src/bigwig_http_vm.erl @@ -13,7 +13,7 @@ init({tcp, http}, Req, _Opts) -> handle(Req, State) -> io:format("===> vm ~p", [all()]), - Body = jsx:term_to_json(all()), + Body = jiffy_ext:encode(all()), io:format("===> vm ~p", [Body]), Headers = [{<<"Content-Type">>, <<"application/json">>}], diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index eaa98fe..a3143ef 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -9,15 +9,16 @@ -module(jiffy_ext). %% API --export([encode/1, - to_jiffy/1]). +-export([encode/1]). %%%=================================================================== %%% API %%%=================================================================== encode(Data) -> + Jiffy=to_jiffy(Data), + io:format("Jiffy is ~p",[Jiffy]), jiffy:encode(Jiffy). %%-------------------------------------------------------------------- %% @doc @@ -27,6 +28,8 @@ encode(Data) -> %%%=================================================================== %%% Internal functions %%%=================================================================== +to_jiffy([First|_]=List) when is_tuple(First), size(First) =:= 2 -> + to_jiffy_props(List,[]); to_jiffy(Tuple) when is_tuple(Tuple) -> to_jiffy_list(tuple_to_list(Tuple),[]); to_jiffy(Port) when is_port(Port) -> @@ -40,13 +43,22 @@ to_jiffy(Values) when is_list(Values) -> to_jiffy(Val) -> Val. +to_jiffy_props([{Key, Value}|Rest], Acc) -> + K = to_jiffy(Key), + V = to_jiffy(Value), + to_jiffy_props(Rest, [{K, V} | Acc]); -to_jiffy_list([], Acc) -> - lists:reverse(Acc); -to_jiffy_list([Val | Rest], Acc0) -> - Acc = [to_jiffy(Val) | Acc0], - to_jiffy_list(Rest, Acc). +to_jiffy_props([Other|Rest], Acc) -> + + to_jiffy_props(Rest,[to_jiffy(Other)|Acc]); +to_jiffy_props([], Acc) -> + {lists:reverse(Acc)}. +to_jiffy_list([], Acc) -> + lists:reverse(Acc); +to_jiffy_list([Val | Rest], Acc0) -> + Acc = [to_jiffy(Val) | Acc0], + to_jiffy_list(Rest, Acc). \ No newline at end of file From 80bbe377a6ce9fd78826877b18ff9fca9ba38f54 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 5 Dec 2013 16:58:35 +0800 Subject: [PATCH 343/405] add jiffy_ext --- src/jiffy_ext.erl | 63 +++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index a3143ef..a05541b 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -16,10 +16,25 @@ %%% API %%%=================================================================== encode(Data) -> + encode(Data, []). + + +encode(Data, Options) -> + ForceUTF8 = lists:member(force_utf8, Options), + case nif_encode(Data, Options) of + {error, invalid_string} when ForceUTF8 == true -> + FixedData = fix(Data), + encode(FixedData, Options -- [force_utf8]); + {error, invalid_object_pair} -> + fix(Data); + {error, _} = Error -> + throw(Error); + {partial, IOData} -> + jiffy:finish_encode(IOData, []); + IOData -> + IOData + end. - Jiffy=to_jiffy(Data), - io:format("Jiffy is ~p",[Jiffy]), - jiffy:encode(Jiffy). %%-------------------------------------------------------------------- %% @doc %% @spec @@ -28,37 +43,25 @@ encode(Data) -> %%%=================================================================== %%% Internal functions %%%=================================================================== -to_jiffy([First|_]=List) when is_tuple(First), size(First) =:= 2 -> - to_jiffy_props(List,[]); -to_jiffy(Tuple) when is_tuple(Tuple) -> +fix(Values) when is_list(Values) -> + fix_array(Values, []); + +fix({K, V}) -> + {fix(K), fix(V)}; +fix(Tuple) when is_tuple(Tuple) -> to_jiffy_list(tuple_to_list(Tuple),[]); -to_jiffy(Port) when is_port(Port) -> +fix(Port) when is_port(Port) -> list_to_binary(erlang:port_to_list(Port)); -to_jiffy(Ref) when is_reference(Ref) -> +fix(Ref) when is_reference(Ref) -> list_to_binary(erlang:ref_to_list(Ref)); -to_jiffy(Pid) when is_pid(Pid) -> +fix(Pid) when is_pid(Pid) -> list_to_binary(erlang:pid_to_list(Pid)); -to_jiffy(Values) when is_list(Values) -> - to_jiffy_list(Values, []); -to_jiffy(Val) -> - Val. - -to_jiffy_props([{Key, Value}|Rest], Acc) -> - K = to_jiffy(Key), - V = to_jiffy(Value), - to_jiffy_props(Rest, [{K, V} | Acc]); - -to_jiffy_props([Other|Rest], Acc) -> - - to_jiffy_props(Rest,[to_jiffy(Other)|Acc]); - -to_jiffy_props([], Acc) -> - {lists:reverse(Acc)}. - +fix(Other) -> + jiffy_utf8:fix(Other). -to_jiffy_list([], Acc) -> +fix_array([], Acc) -> lists:reverse(Acc); -to_jiffy_list([Val | Rest], Acc0) -> - Acc = [to_jiffy(Val) | Acc0], - to_jiffy_list(Rest, Acc). \ No newline at end of file +fix_array([Val | Rest], Acc0) -> + Acc = [fix(Val) | Acc0], + fix_array(Rest, Acc). From c25e41cd0ad7f213a58e22548ed614e6776a3569 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 5 Dec 2013 16:58:35 +0800 Subject: [PATCH 344/405] add jiffy_ext --- src/jiffy_ext.erl | 63 +++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index a3143ef..a05541b 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -16,10 +16,25 @@ %%% API %%%=================================================================== encode(Data) -> + encode(Data, []). + + +encode(Data, Options) -> + ForceUTF8 = lists:member(force_utf8, Options), + case nif_encode(Data, Options) of + {error, invalid_string} when ForceUTF8 == true -> + FixedData = fix(Data), + encode(FixedData, Options -- [force_utf8]); + {error, invalid_object_pair} -> + fix(Data); + {error, _} = Error -> + throw(Error); + {partial, IOData} -> + jiffy:finish_encode(IOData, []); + IOData -> + IOData + end. - Jiffy=to_jiffy(Data), - io:format("Jiffy is ~p",[Jiffy]), - jiffy:encode(Jiffy). %%-------------------------------------------------------------------- %% @doc %% @spec @@ -28,37 +43,25 @@ encode(Data) -> %%%=================================================================== %%% Internal functions %%%=================================================================== -to_jiffy([First|_]=List) when is_tuple(First), size(First) =:= 2 -> - to_jiffy_props(List,[]); -to_jiffy(Tuple) when is_tuple(Tuple) -> +fix(Values) when is_list(Values) -> + fix_array(Values, []); + +fix({K, V}) -> + {fix(K), fix(V)}; +fix(Tuple) when is_tuple(Tuple) -> to_jiffy_list(tuple_to_list(Tuple),[]); -to_jiffy(Port) when is_port(Port) -> +fix(Port) when is_port(Port) -> list_to_binary(erlang:port_to_list(Port)); -to_jiffy(Ref) when is_reference(Ref) -> +fix(Ref) when is_reference(Ref) -> list_to_binary(erlang:ref_to_list(Ref)); -to_jiffy(Pid) when is_pid(Pid) -> +fix(Pid) when is_pid(Pid) -> list_to_binary(erlang:pid_to_list(Pid)); -to_jiffy(Values) when is_list(Values) -> - to_jiffy_list(Values, []); -to_jiffy(Val) -> - Val. - -to_jiffy_props([{Key, Value}|Rest], Acc) -> - K = to_jiffy(Key), - V = to_jiffy(Value), - to_jiffy_props(Rest, [{K, V} | Acc]); - -to_jiffy_props([Other|Rest], Acc) -> - - to_jiffy_props(Rest,[to_jiffy(Other)|Acc]); - -to_jiffy_props([], Acc) -> - {lists:reverse(Acc)}. - +fix(Other) -> + jiffy_utf8:fix(Other). -to_jiffy_list([], Acc) -> +fix_array([], Acc) -> lists:reverse(Acc); -to_jiffy_list([Val | Rest], Acc0) -> - Acc = [to_jiffy(Val) | Acc0], - to_jiffy_list(Rest, Acc). \ No newline at end of file +fix_array([Val | Rest], Acc0) -> + Acc = [fix(Val) | Acc0], + fix_array(Rest, Acc). From 44ebea1145a4d08a01298fbee05496da6afc4f19 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Thu, 5 Dec 2013 18:00:56 +0800 Subject: [PATCH 345/405] update jiffy repo --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 295ad12..3fbf077 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,7 @@ {deps, [ {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, - {jiffy, ".*", {git, "git://github.com/davisp/jiffy", "master"}}, + {jiffy, ".*", {git, "git://github.com/jacktang/jiffy", "master"}}, {jsx, ".*", {git, "https://github.com/yuzhaoren/jsx.git", {branch, "beamspirit"}}}, {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}} %%{jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} From dc21a07cd2a8d7f77ca4bf634876b31c36fb6e80 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 9 Dec 2013 14:13:28 +0800 Subject: [PATCH 346/405] support R15B --- rebar.config | 2 +- src/bigwig.erl | 22 ++++++++++++------- src/bigwig_http_lager.erl | 10 ++++----- src/bigwig_http_module.erl | 8 +++---- src/jiffy_ext.erl | 43 +++----------------------------------- 5 files changed, 28 insertions(+), 57 deletions(-) diff --git a/rebar.config b/rebar.config index 3fbf077..a6a981a 100644 --- a/rebar.config +++ b/rebar.config @@ -1,7 +1,7 @@ {erl_opts, [debug_info, fail_on_warning, {parse_transform, lager_transform }]}. -{require_otp_vsn, "R16"}. +{require_otp_vsn, "R15"}. {deps_dir, ["deps"]}. {deps, [ diff --git a/src/bigwig.erl b/src/bigwig.erl index 6fdee28..bdd71bf 100644 --- a/src/bigwig.erl +++ b/src/bigwig.erl @@ -1,15 +1,23 @@ -module(bigwig). -export([start/0, stop/0]). +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. + start() -> - application:ensure_started(ranch), - application:ensure_started(jsx), + ensure_started(ranch), + ensure_started(jsx), - application:ensure_started(crypto), - application:ensure_started(sasl), - application:ensure_started(cowlib), - application:ensure_started(cowboy), - application:ensure_started(lager), + ensure_started(crypto), + ensure_started(sasl), + ensure_started(cowlib), + ensure_started(cowboy), + %ensure_started(lager), application:start(bigwig). stop() -> diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index e57a6d5..e1b4f38 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -47,16 +47,16 @@ handle_add_tracer(Tracer, Req, State) -> 5 -> Attr1 = binary_to_atom(lists:nth(1, All), utf8), RoutingKey = lists:nth(2, All), - Filter = [{binary_to_atom(lists:nth(3, All), utf8), binary_to_integer(lists:nth(4, All))}], + Filter = [{binary_to_atom(lists:nth(3, All), utf8), list_to_integer(binary_to_list(lists:nth(4, All)))}], Level = binary_to_atom(lists:nth(5, All), utf8), amqp_tracer:trace_amqp(Attr1, RoutingKey, Filter, Level); 3 -> RoutingKey = lists:nth(1, All), - Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + Filter = [{binary_to_atom(lists:nth(2, All), utf8), list_to_integer(binary_to_list(lists:nth(3, All)))}], amqp_tracer:trace_amqp(RoutingKey, Filter); 4 -> RoutingKey = lists:nth(1, All), - Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + Filter = [{binary_to_atom(lists:nth(2, All), utf8), list_to_integer(binary_to_list(lists:nth(3, All)))}], Level = binary_to_atom(lists:nth(4, All), utf8), amqp_tracer:trace_amqp(RoutingKey, Filter, Level); _ -> ok @@ -71,7 +71,7 @@ handle_del_trace(Tracer, Req, State) -> 5 -> Attr1 = binary_to_atom(lists:nth(1, All), utf8), RoutingKey = lists:nth(2, All), - Filter = [{binary_to_atom(lists:nth(3, All), utf8), binary_to_integer(lists:nth(4, All))}], + Filter = [{binary_to_atom(lists:nth(3, All), utf8), list_to_integer(binary_to_list(lists:nth(4, All)))}], Level = binary_to_atom(lists:nth(5, All), utf8), Trace0 = { Filter, Level, {lager_amqp_backend, RoutingKey} }, case lager_util:validate_trace(Trace0) of @@ -82,7 +82,7 @@ handle_del_trace(Tracer, Req, State) -> end; 4 -> RoutingKey = lists:nth(1, All), - Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + Filter = [{binary_to_atom(lists:nth(2, All), utf8), list_to_integer(binary_to_list(lists:nth(3, All)))}], Level = binary_to_atom(lists:nth(4, All), utf8), Trace0 = { Filter, Level, {lager_amqp_backend, RoutingKey} }, case lager_util:validate_trace(Trace0) of diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index 8780bc9..b08a5af 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -56,9 +56,9 @@ to_module_info(Bin) -> T when is_tuple(T) -> {Year,Month,Day,Hour,Minute,Sec}=T, proplists:delete(compile, L) ++ - [ {compile, [ {time, [{'_type',<<"date">>},{data,[integer_to_binary(Year) - ,integer_to_binary(Month),integer_to_binary(Day),integer_to_binary(Hour), - integer_to_binary(Minute),integer_to_binary(Sec)]}]}]} + [ {compile, [ {time, [{'_type',<<"date">>},{data,[list_to_binary(integer_to_list(Year)) + ,list_to_binary(integer_to_list(Month)),list_to_binary(integer_to_list(Day)),list_to_binary(integer_to_list(Hour)), + list_to_binary(integer_to_list(Minute)),list_to_binary(integer_to_list(Sec))]}]}]} | proplists:delete(time, L) ]; _ -> ModInfo2 @@ -97,7 +97,7 @@ to_module_info(Bin) -> json_response(Info, Req, State) -> - Body = jiffy_ext:encode(Info), + Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index a05541b..724570e 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -15,25 +15,10 @@ %%%=================================================================== %%% API %%%=================================================================== +encode([First|_]=List) when is_tuple(First),size(First)=:=2 -> + jiffy:encode({List}); encode(Data) -> - encode(Data, []). - - -encode(Data, Options) -> - ForceUTF8 = lists:member(force_utf8, Options), - case nif_encode(Data, Options) of - {error, invalid_string} when ForceUTF8 == true -> - FixedData = fix(Data), - encode(FixedData, Options -- [force_utf8]); - {error, invalid_object_pair} -> - fix(Data); - {error, _} = Error -> - throw(Error); - {partial, IOData} -> - jiffy:finish_encode(IOData, []); - IOData -> - IOData - end. + jiffy:encode(Data). %%-------------------------------------------------------------------- %% @doc @@ -43,25 +28,3 @@ encode(Data, Options) -> %%%=================================================================== %%% Internal functions %%%=================================================================== -fix(Values) when is_list(Values) -> - fix_array(Values, []); - -fix({K, V}) -> - {fix(K), fix(V)}; -fix(Tuple) when is_tuple(Tuple) -> - to_jiffy_list(tuple_to_list(Tuple),[]); -fix(Port) when is_port(Port) -> - list_to_binary(erlang:port_to_list(Port)); -fix(Ref) when is_reference(Ref) -> - list_to_binary(erlang:ref_to_list(Ref)); -fix(Pid) when is_pid(Pid) -> - list_to_binary(erlang:pid_to_list(Pid)); -fix(Other) -> - jiffy_utf8:fix(Other). - - -fix_array([], Acc) -> - lists:reverse(Acc); -fix_array([Val | Rest], Acc0) -> - Acc = [fix(Val) | Acc0], - fix_array(Rest, Acc). From db2299ca289a16283f0a22a99c9e83859396c062 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 9 Dec 2013 14:13:28 +0800 Subject: [PATCH 347/405] support R15B --- src/bigwig.erl | 22 ++++++++++++------- src/bigwig_http_lager.erl | 10 ++++----- src/bigwig_http_module.erl | 8 +++---- src/jiffy_ext.erl | 43 +++----------------------------------- 4 files changed, 27 insertions(+), 56 deletions(-) diff --git a/src/bigwig.erl b/src/bigwig.erl index 6fdee28..bdd71bf 100644 --- a/src/bigwig.erl +++ b/src/bigwig.erl @@ -1,15 +1,23 @@ -module(bigwig). -export([start/0, stop/0]). +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. + start() -> - application:ensure_started(ranch), - application:ensure_started(jsx), + ensure_started(ranch), + ensure_started(jsx), - application:ensure_started(crypto), - application:ensure_started(sasl), - application:ensure_started(cowlib), - application:ensure_started(cowboy), - application:ensure_started(lager), + ensure_started(crypto), + ensure_started(sasl), + ensure_started(cowlib), + ensure_started(cowboy), + %ensure_started(lager), application:start(bigwig). stop() -> diff --git a/src/bigwig_http_lager.erl b/src/bigwig_http_lager.erl index e57a6d5..e1b4f38 100644 --- a/src/bigwig_http_lager.erl +++ b/src/bigwig_http_lager.erl @@ -47,16 +47,16 @@ handle_add_tracer(Tracer, Req, State) -> 5 -> Attr1 = binary_to_atom(lists:nth(1, All), utf8), RoutingKey = lists:nth(2, All), - Filter = [{binary_to_atom(lists:nth(3, All), utf8), binary_to_integer(lists:nth(4, All))}], + Filter = [{binary_to_atom(lists:nth(3, All), utf8), list_to_integer(binary_to_list(lists:nth(4, All)))}], Level = binary_to_atom(lists:nth(5, All), utf8), amqp_tracer:trace_amqp(Attr1, RoutingKey, Filter, Level); 3 -> RoutingKey = lists:nth(1, All), - Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + Filter = [{binary_to_atom(lists:nth(2, All), utf8), list_to_integer(binary_to_list(lists:nth(3, All)))}], amqp_tracer:trace_amqp(RoutingKey, Filter); 4 -> RoutingKey = lists:nth(1, All), - Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + Filter = [{binary_to_atom(lists:nth(2, All), utf8), list_to_integer(binary_to_list(lists:nth(3, All)))}], Level = binary_to_atom(lists:nth(4, All), utf8), amqp_tracer:trace_amqp(RoutingKey, Filter, Level); _ -> ok @@ -71,7 +71,7 @@ handle_del_trace(Tracer, Req, State) -> 5 -> Attr1 = binary_to_atom(lists:nth(1, All), utf8), RoutingKey = lists:nth(2, All), - Filter = [{binary_to_atom(lists:nth(3, All), utf8), binary_to_integer(lists:nth(4, All))}], + Filter = [{binary_to_atom(lists:nth(3, All), utf8), list_to_integer(binary_to_list(lists:nth(4, All)))}], Level = binary_to_atom(lists:nth(5, All), utf8), Trace0 = { Filter, Level, {lager_amqp_backend, RoutingKey} }, case lager_util:validate_trace(Trace0) of @@ -82,7 +82,7 @@ handle_del_trace(Tracer, Req, State) -> end; 4 -> RoutingKey = lists:nth(1, All), - Filter = [{binary_to_atom(lists:nth(2, All), utf8), binary_to_integer(lists:nth(3, All))}], + Filter = [{binary_to_atom(lists:nth(2, All), utf8), list_to_integer(binary_to_list(lists:nth(3, All)))}], Level = binary_to_atom(lists:nth(4, All), utf8), Trace0 = { Filter, Level, {lager_amqp_backend, RoutingKey} }, case lager_util:validate_trace(Trace0) of diff --git a/src/bigwig_http_module.erl b/src/bigwig_http_module.erl index 8780bc9..b08a5af 100644 --- a/src/bigwig_http_module.erl +++ b/src/bigwig_http_module.erl @@ -56,9 +56,9 @@ to_module_info(Bin) -> T when is_tuple(T) -> {Year,Month,Day,Hour,Minute,Sec}=T, proplists:delete(compile, L) ++ - [ {compile, [ {time, [{'_type',<<"date">>},{data,[integer_to_binary(Year) - ,integer_to_binary(Month),integer_to_binary(Day),integer_to_binary(Hour), - integer_to_binary(Minute),integer_to_binary(Sec)]}]}]} + [ {compile, [ {time, [{'_type',<<"date">>},{data,[list_to_binary(integer_to_list(Year)) + ,list_to_binary(integer_to_list(Month)),list_to_binary(integer_to_list(Day)),list_to_binary(integer_to_list(Hour)), + list_to_binary(integer_to_list(Minute)),list_to_binary(integer_to_list(Sec))]}]}]} | proplists:delete(time, L) ]; _ -> ModInfo2 @@ -97,7 +97,7 @@ to_module_info(Bin) -> json_response(Info, Req, State) -> - Body = jiffy_ext:encode(Info), + Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State}. diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index a05541b..724570e 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -15,25 +15,10 @@ %%%=================================================================== %%% API %%%=================================================================== +encode([First|_]=List) when is_tuple(First),size(First)=:=2 -> + jiffy:encode({List}); encode(Data) -> - encode(Data, []). - - -encode(Data, Options) -> - ForceUTF8 = lists:member(force_utf8, Options), - case nif_encode(Data, Options) of - {error, invalid_string} when ForceUTF8 == true -> - FixedData = fix(Data), - encode(FixedData, Options -- [force_utf8]); - {error, invalid_object_pair} -> - fix(Data); - {error, _} = Error -> - throw(Error); - {partial, IOData} -> - jiffy:finish_encode(IOData, []); - IOData -> - IOData - end. + jiffy:encode(Data). %%-------------------------------------------------------------------- %% @doc @@ -43,25 +28,3 @@ encode(Data, Options) -> %%%=================================================================== %%% Internal functions %%%=================================================================== -fix(Values) when is_list(Values) -> - fix_array(Values, []); - -fix({K, V}) -> - {fix(K), fix(V)}; -fix(Tuple) when is_tuple(Tuple) -> - to_jiffy_list(tuple_to_list(Tuple),[]); -fix(Port) when is_port(Port) -> - list_to_binary(erlang:port_to_list(Port)); -fix(Ref) when is_reference(Ref) -> - list_to_binary(erlang:ref_to_list(Ref)); -fix(Pid) when is_pid(Pid) -> - list_to_binary(erlang:pid_to_list(Pid)); -fix(Other) -> - jiffy_utf8:fix(Other). - - -fix_array([], Acc) -> - lists:reverse(Acc); -fix_array([Val | Rest], Acc0) -> - Acc = [fix(Val) | Acc0], - fix_array(Rest, Acc). From 8bc9926b2715699010f5b4c432cc8ee350b49db2 Mon Sep 17 00:00:00 2001 From: yuzhaoren <184923830@qq.com> Date: Mon, 9 Dec 2013 17:17:19 +0800 Subject: [PATCH 348/405] Delete rebar.config --- rebar.config | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 rebar.config diff --git a/rebar.config b/rebar.config deleted file mode 100644 index a6a981a..0000000 --- a/rebar.config +++ /dev/null @@ -1,14 +0,0 @@ -{erl_opts, [debug_info, fail_on_warning, - {parse_transform, lager_transform }]}. - -{require_otp_vsn, "R15"}. -{deps_dir, ["deps"]}. - -{deps, [ - {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, - {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, - {jiffy, ".*", {git, "git://github.com/jacktang/jiffy", "master"}}, - {jsx, ".*", {git, "https://github.com/yuzhaoren/jsx.git", {branch, "beamspirit"}}}, - {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}} - %%{jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} -]}. From 467ffd92947a1b472c0f167b74d398c64ef02620 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 9 Dec 2013 17:18:09 +0800 Subject: [PATCH 349/405] add jsx beamspirit --- rebar.config | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index a6a981a..d6fdb1f 100644 --- a/rebar.config +++ b/rebar.config @@ -8,7 +8,6 @@ {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, {jiffy, ".*", {git, "git://github.com/jacktang/jiffy", "master"}}, - {jsx, ".*", {git, "https://github.com/yuzhaoren/jsx.git", {branch, "beamspirit"}}}, + {jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}}, {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}} - %%{jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}} ]}. From 737781161a93bc6f64d4e93ca9347f322f8e5232 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 9 Dec 2013 17:21:49 +0800 Subject: [PATCH 350/405] d From 441fe66aa599f0d544bfaf622022bb8b0d6663f7 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 9 Dec 2013 17:24:59 +0800 Subject: [PATCH 351/405] add jsx beamspirit --- rebar.config | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 rebar.config diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..0cba54e --- /dev/null +++ b/rebar.config @@ -0,0 +1,13 @@ +{erl_opts, [debug_info, fail_on_warning, + {parse_transform, lager_transform }]}. + +{require_otp_vsn, "R15"}. +{deps_dir, ["deps"]}. + +{deps, [ + {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, + {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, + {jiffy, ".*", {git, "git://github.com/jacktang/jiffy", "master"}}, + {jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}}, + {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}} +]}. \ No newline at end of file From 9e1601d6bb8aa234c3583b6ec03f03e7cafe7290 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 9 Dec 2013 17:26:22 +0800 Subject: [PATCH 352/405] beamspirit --- src/bigwig_http_pid.erl | 2 +- src/bigwig_http_vm.erl | 2 +- src/jiffy_ext.erl | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index f20485d..81ee80b 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -91,7 +91,7 @@ pid_response(Pid, Req, State) -> Info -> % Info1=lists:map(fun(T) -> to_json(T) end,Info), % Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), - Body = jiffy_ext:encode(Info), + Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} diff --git a/src/bigwig_http_vm.erl b/src/bigwig_http_vm.erl index 87fd0ce..d11b8a8 100644 --- a/src/bigwig_http_vm.erl +++ b/src/bigwig_http_vm.erl @@ -13,7 +13,7 @@ init({tcp, http}, Req, _Opts) -> handle(Req, State) -> io:format("===> vm ~p", [all()]), - Body = jiffy_ext:encode(all()), + Body = jsx:term_to_json(all()), io:format("===> vm ~p", [Body]), Headers = [{<<"Content-Type">>, <<"application/json">>}], diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index 724570e..0c30e84 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -15,8 +15,8 @@ %%%=================================================================== %%% API %%%=================================================================== -encode([First|_]=List) when is_tuple(First),size(First)=:=2 -> - jiffy:encode({List}); +%encode([First|_]=List) when is_tuple(First),size(First)=:=2 -> +% jiffy:encode({List}); encode(Data) -> jiffy:encode(Data). From 90db1873c2275d99ba72e37254272b4ec1fa8318 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 9 Dec 2013 17:26:22 +0800 Subject: [PATCH 353/405] beamspirit --- src/bigwig_http_pid.erl | 2 +- src/bigwig_http_vm.erl | 2 +- src/jiffy_ext.erl | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bigwig_http_pid.erl b/src/bigwig_http_pid.erl index f20485d..81ee80b 100644 --- a/src/bigwig_http_pid.erl +++ b/src/bigwig_http_pid.erl @@ -91,7 +91,7 @@ pid_response(Pid, Req, State) -> Info -> % Info1=lists:map(fun(T) -> to_json(T) end,Info), % Info2=lists:keydelete(dictionary,1,lists:keydelete(links,1,Info1)), - Body = jiffy_ext:encode(Info), + Body = jsx:term_to_json(Info), Headers = [{<<"Content-Type">>, <<"application/json">>}], {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), {ok, Req2, State} diff --git a/src/bigwig_http_vm.erl b/src/bigwig_http_vm.erl index 87fd0ce..d11b8a8 100644 --- a/src/bigwig_http_vm.erl +++ b/src/bigwig_http_vm.erl @@ -13,7 +13,7 @@ init({tcp, http}, Req, _Opts) -> handle(Req, State) -> io:format("===> vm ~p", [all()]), - Body = jiffy_ext:encode(all()), + Body = jsx:term_to_json(all()), io:format("===> vm ~p", [Body]), Headers = [{<<"Content-Type">>, <<"application/json">>}], diff --git a/src/jiffy_ext.erl b/src/jiffy_ext.erl index 724570e..0c30e84 100644 --- a/src/jiffy_ext.erl +++ b/src/jiffy_ext.erl @@ -15,8 +15,8 @@ %%%=================================================================== %%% API %%%=================================================================== -encode([First|_]=List) when is_tuple(First),size(First)=:=2 -> - jiffy:encode({List}); +%encode([First|_]=List) when is_tuple(First),size(First)=:=2 -> +% jiffy:encode({List}); encode(Data) -> jiffy:encode(Data). From 35f0295a7e275baab8d4a706b42a46f5687b0448 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 9 Dec 2013 17:41:04 +0800 Subject: [PATCH 354/405] rebar.config --- rebar.config | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 rebar.config diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..0cba54e --- /dev/null +++ b/rebar.config @@ -0,0 +1,13 @@ +{erl_opts, [debug_info, fail_on_warning, + {parse_transform, lager_transform }]}. + +{require_otp_vsn, "R15"}. +{deps_dir, ["deps"]}. + +{deps, [ + {lager, ".*", {git, "git://github.com/basho/lager.git", "master"}}, + {cowboy, ".*", {git, "git://github.com/extend/cowboy.git", "master"}}, + {jiffy, ".*", {git, "git://github.com/jacktang/jiffy", "master"}}, + {jsx, ".*", {git, "https://github.com/spawnfest/jsx.git", {branch, "beamspirit"}}}, + {'lager_amqp_backend', ".*", {git, "git://github.com/jacktang/lager_amqp_backend.git", "master"}} +]}. \ No newline at end of file From 063cd919e4cef43b76c08b74fd22dc1c4a8dd76c Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 9 Dec 2013 18:36:56 +0800 Subject: [PATCH 355/405] modify otp_vsn in rebar.config --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 0cba54e..440d51d 100644 --- a/rebar.config +++ b/rebar.config @@ -1,7 +1,7 @@ {erl_opts, [debug_info, fail_on_warning, {parse_transform, lager_transform }]}. -{require_otp_vsn, "R15"}. +{require_otp_vsn, "R15|R16"}. {deps_dir, ["deps"]}. {deps, [ From 232dba708dca103f6b5931bbdf82ba5ed9cd802b Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Tue, 10 Dec 2013 17:01:32 +0800 Subject: [PATCH 356/405] #5147 add entop --- priv/html/index.html | 43 ++++++++++++++++++++------- priv/js/et.js | 26 +++++++++++++++++ src/bigwig_app.erl | 1 + src/bigwig_http_entop.erl | 61 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 src/bigwig_http_entop.erl diff --git a/priv/html/index.html b/priv/html/index.html index 9018959..e89d7a9 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -18,6 +18,8 @@

    BigWig

  • Processes
  • Applications
  • Lager Logging
  • +
  • Entop
  • +
  • @@ -112,16 +114,6 @@

    BigWig

    - - - - Accumulate Pause @@ -143,6 +135,35 @@

    BigWig

    +
    +
    + + + + +
    + + + + + + + + + + + + + + +
    PidRegistered NameReductionsMQueueHSizeSSizeHTot
    +
    Tracer: @@ -155,7 +176,7 @@

    BigWig

    - trace + trace diff --git a/priv/js/et.js b/priv/js/et.js index 826a0a9..5107305 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -57,3 +57,29 @@ $(document).ready(function() { } }); }); + +$(document).ready(function() { + var oTable = $('#entop_table').dataTable({ + "bProcessing": false, + "iDisplayLength": 25, + "sAjaxSource": '/entop', + "aaSorting": [[5, "desc"]], + // Get data piggy-backed on the main table and update the page + "fnServerData": function(sSource, aaData, fnCallback) { + $.getJSON( sSource, aaData, function(json) { + // Use our RENDERER to format everything + for(var i=0; i]/g, ''); + //json.aaData[i][0] = ($('<'+Pid+'>'))[0].outerHTML; + var Pid = json.aaData[i][0]; + json.aaData[i][0]= ($(''+Pid+''))[0].outerHTML; + json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; + } + fnCallback(json); + }); + } + }); + $(document).everyTime(5000, "poll_etop", function() {oTable.fnReloadAjax();}); +}); \ No newline at end of file diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index d8c65a6..4f4c979 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -22,6 +22,7 @@ start(_StartType, _StartArgs) -> {"/rb/[...]", bigwig_http_rb, []}, {"/pid/[...]", bigwig_http_pid, []}, {"/top/[...]", bigwig_http_etop2, []}, + {"/entop/[...]", bigwig_http_entop, []}, {"/appmon/[...]", bigwig_http_appmon, []}, {"/module/[...]", bigwig_http_module, []}, {"/stats-stream", bigwig_http_stats_stream, []}, diff --git a/src/bigwig_http_entop.erl b/src/bigwig_http_entop.erl new file mode 100644 index 0000000..b4be4af --- /dev/null +++ b/src/bigwig_http_entop.erl @@ -0,0 +1,61 @@ +%% +%% show details on a specific process +%% +-module(bigwig_http_entop). +-behaviour(cowboy_http_handler). +-export([init/3, handle/2, terminate/3]). + +init({tcp, http}, Req, _Opts) -> + {ok, Req, no_state}. + +handle(Req0, State) -> + {Path, Req} = cowboy_req:path(Req0), + {Method, Req1} = cowboy_req:method(Req), + + handle_path(Method, Path, Req1, State). + +handle_path(<<"GET">>, <<"/entop">>, Req, State) -> + handle_get(Req, State); +handle_path(_, _, Req, State) -> + not_found(Req, State). + +not_found(Req, State) -> + {ok, Req2} = cowboy_req:reply(404, [], <<"

    404

    ">>, Req), + {ok, Req2, State}. + +terminate(_Reason, _Req, _State) -> + ok. + +handle_get(Req, State) -> + Headers = [{<<"Content-Type">>, <<"application/json">>}], + Info = update(), + Body = jsx:term_to_json(Info), + {ok, Req2} = cowboy_req:reply(200, Headers, Body, Req), + {ok, Req2, State}. + +update() -> + PidList=erlang:processes(), + Ps = [entop_proc_info_to_json(P) || P <- PidList], + NumPs = length(Ps), + [{<<"iTotalRecords">>, NumPs}, + {<<"iTotalDisplayRecords">>, NumPs}, + {<<"sColumns">>, <<"pid,name,reds,mq,hsize,ssize,htot">>}, + {<<"aaData">>, Ps}]. +entop_proc_info_to_json(P) -> + ProcInfo = erlang:process_info(P, [registered_name, + reductions, + message_queue_len, + heap_size, + stack_size, + total_heap_size]), + case ProcInfo of + undefined -> []; + _ -> + Name = proplists:get_value(registered_name, ProcInfo, undefined), + Reds = proplists:get_value(reductions, ProcInfo, 0), + Mq = proplists:get_value(message_queue_len, ProcInfo, 0), + Hsize = proplists:get_value(heap_size, ProcInfo, 0), + Ssize = proplists:get_value(stack_size, ProcInfo, 0), + Htot = proplists:get_value(total_heap_size, ProcInfo, 0), + [ list_to_binary(pid_to_list(P)), Name, Reds, Mq, Hsize, Ssize, Htot ] + end. From deea2d4940d3cfcc8604f769fdf51228519f1c08 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Wed, 11 Dec 2013 14:23:44 +0800 Subject: [PATCH 357/405] entop --- priv/html/index.html | 4 ++++ priv/js/bigwig.js | 1 + priv/js/et.js | 6 ++++-- priv/js/lager.js | 3 ++- src/bigwig_http_entop.erl | 13 ++++++++++--- src/bigwig_http_lager_stream.erl | 2 -- 6 files changed, 21 insertions(+), 8 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index e89d7a9..ef30542 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -148,6 +148,10 @@

    BigWig

    +
    + + +
    diff --git a/priv/js/bigwig.js b/priv/js/bigwig.js index d839a5b..2ba6071 100644 --- a/priv/js/bigwig.js +++ b/priv/js/bigwig.js @@ -153,6 +153,7 @@ function sendTxt(txt) { closeSubscribe(); $(this).remove(); window.location.reload(); + // window.location.href="lager"; }, buttons: { "Close": function() { $(this).dialog("close"); }, diff --git a/priv/js/et.js b/priv/js/et.js index 5107305..c80c4ba 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -74,12 +74,14 @@ $(document).ready(function() { //var Pid = (json.aaData[i][0]).data.replace(/[<>]/g, ''); //json.aaData[i][0] = ($('<'+Pid+'>'))[0].outerHTML; var Pid = json.aaData[i][0]; - json.aaData[i][0]= ($(''+Pid+''))[0].outerHTML; + json.aaData[i][0]= RENDERER.render_json_val(json.aaData[i][0])[0].outerHTML; json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; } fnCallback(json); }); } }); - $(document).everyTime(5000, "poll_etop", function() {oTable.fnReloadAjax();}); + $(document).everyTime(2000, "poll_etop", function() {oTable.fnReloadAjax();}); + $('#start_node').bind('click', function() { + }); }); \ No newline at end of file diff --git a/priv/js/lager.js b/priv/js/lager.js index 85d1532..de742be 100644 --- a/priv/js/lager.js +++ b/priv/js/lager.js @@ -1,6 +1,7 @@ $(document).ready(function(){ -} +}); + function connect(to) { var host = document.location.host; diff --git a/src/bigwig_http_entop.erl b/src/bigwig_http_entop.erl index b4be4af..7ffc672 100644 --- a/src/bigwig_http_entop.erl +++ b/src/bigwig_http_entop.erl @@ -34,7 +34,8 @@ handle_get(Req, State) -> {ok, Req2, State}. update() -> - PidList=erlang:processes(), + %%PidList=erlang:processes(), + PidList = rpc:call('strategy_agent@strategy.lk.com', erlang, processes, []), Ps = [entop_proc_info_to_json(P) || P <- PidList], NumPs = length(Ps), [{<<"iTotalRecords">>, NumPs}, @@ -42,12 +43,18 @@ update() -> {<<"sColumns">>, <<"pid,name,reds,mq,hsize,ssize,htot">>}, {<<"aaData">>, Ps}]. entop_proc_info_to_json(P) -> - ProcInfo = erlang:process_info(P, [registered_name, + %%ProcInfo = erlang:process_info(P, [registered_name, + %% reductions, + %% message_queue_len, + %% heap_size, + %% stack_size, + %% total_heap_size]), + ProcInfo = rpc:call('strategy_agent@strategy.lk.com', erlang, process_info, [P, [registered_name, reductions, message_queue_len, heap_size, stack_size, - total_heap_size]), + total_heap_size]]), case ProcInfo of undefined -> []; _ -> diff --git a/src/bigwig_http_lager_stream.erl b/src/bigwig_http_lager_stream.erl index 95edaf6..d0e6429 100644 --- a/src/bigwig_http_lager_stream.erl +++ b/src/bigwig_http_lager_stream.erl @@ -24,8 +24,6 @@ websocket_init(_TransportName, Req, _Opts) -> websocket_handle({text, Msg}, Req, State) when Msg =:= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), amqp_subscriber:cast(unsubscribe), - %% Pid = State, - %% erlang:exit(Pid,kill), {ok, Req, State}; websocket_handle({text, Msg}, Req, State) when Msg =/= <<"unsubscribe">> -> io:format("msg is ~p",[Msg]), From d925df9d26bfae8a2e7e7953d3bed8f8bceed22f Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Wed, 11 Dec 2013 17:59:41 +0800 Subject: [PATCH 358/405] entop --- priv/html/index.html | 12 ------------ priv/js/et.js | 4 +--- src/bigwig_http_entop.erl | 20 ++++++++++---------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index ef30542..131630f 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -136,18 +136,6 @@

    BigWig

    -
    - - - - -
    diff --git a/priv/js/et.js b/priv/js/et.js index c80c4ba..d40a601 100644 --- a/priv/js/et.js +++ b/priv/js/et.js @@ -74,7 +74,7 @@ $(document).ready(function() { //var Pid = (json.aaData[i][0]).data.replace(/[<>]/g, ''); //json.aaData[i][0] = ($('<'+Pid+'>'))[0].outerHTML; var Pid = json.aaData[i][0]; - json.aaData[i][0]= RENDERER.render_json_val(json.aaData[i][0])[0].outerHTML; + json.aaData[i][0]= ($(''+Pid+''))[0].outerHTML; json.aaData[i][1] = RENDERER.render_json_val(json.aaData[i][1])[0].outerHTML; } fnCallback(json); @@ -82,6 +82,4 @@ $(document).ready(function() { } }); $(document).everyTime(2000, "poll_etop", function() {oTable.fnReloadAjax();}); - $('#start_node').bind('click', function() { - }); }); \ No newline at end of file diff --git a/src/bigwig_http_entop.erl b/src/bigwig_http_entop.erl index 7ffc672..4c1f5a3 100644 --- a/src/bigwig_http_entop.erl +++ b/src/bigwig_http_entop.erl @@ -34,8 +34,8 @@ handle_get(Req, State) -> {ok, Req2, State}. update() -> - %%PidList=erlang:processes(), - PidList = rpc:call('strategy_agent@strategy.lk.com', erlang, processes, []), + PidList=erlang:processes(), + %%PidList = rpc:call('strategy_agent@strategy.lk.com', erlang, processes, []), Ps = [entop_proc_info_to_json(P) || P <- PidList], NumPs = length(Ps), [{<<"iTotalRecords">>, NumPs}, @@ -43,18 +43,18 @@ update() -> {<<"sColumns">>, <<"pid,name,reds,mq,hsize,ssize,htot">>}, {<<"aaData">>, Ps}]. entop_proc_info_to_json(P) -> - %%ProcInfo = erlang:process_info(P, [registered_name, - %% reductions, - %% message_queue_len, - %% heap_size, - %% stack_size, - %% total_heap_size]), - ProcInfo = rpc:call('strategy_agent@strategy.lk.com', erlang, process_info, [P, [registered_name, + ProcInfo = erlang:process_info(P, [registered_name, reductions, message_queue_len, heap_size, stack_size, - total_heap_size]]), + total_heap_size]), + %ProcInfo = rpc:call('strategy_agent@strategy.lk.com', erlang, process_info, [P, [registered_name, + % reductions, + % message_queue_len, + % heap_size, + % stack_size, + % total_heap_size]]), case ProcInfo of undefined -> []; _ -> From 68834fdae0c920b1f69db0623073483d091f286f Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 30 Dec 2013 18:05:11 +0800 Subject: [PATCH 359/405] #5200 --- src/bigwig_http_md_stream.erl | 43 ++++++ src/md_amqp_subscriber.erl | 245 ++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+) create mode 100644 src/bigwig_http_md_stream.erl create mode 100644 src/md_amqp_subscriber.erl diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl new file mode 100644 index 0000000..cd5ee9a --- /dev/null +++ b/src/bigwig_http_md_stream.erl @@ -0,0 +1,43 @@ +%% Websocket connection that sends log of trace +%% +-module(bigwig_http_md_stream). +-behaviour(cowboy_websocket_handler). + +-export([init/3, handle/2, terminate/3]). +-export([websocket_init/3, websocket_handle/3, + websocket_info/3, websocket_terminate/3]). + +init({tcp, http}, _Req, _Opts) -> + {upgrade, protocol, cowboy_websocket}. + +handle(_Req, _State) -> + exit(websockets_only). + +terminate(_Reason, _Req, _State) -> + exit(websockets_only). + +websocket_init(_TransportName, Req, _Opts) -> + bigwig_pubsubhub:register_client(self()), + {ok, Req, undefined_state}. + +%% TODO handle stuff like {bigwig, {appmon, ... }} and send that too + +websocket_info({bigwig, {market_dispather, {NodeSubCount, _NodeSubDetail}}}, Req, State) -> + NodeSubCountList = dict:to_list(NodeSubCount), + io:format("NodeSubCount is ~p ~n", [NodeSubCountList]), + Reply = jsx:term_to_json([{market, NodeSubCountList}]), + {reply, {text, Reply}, Req, State}; + + +websocket_info({bigwig, _}, Req, State) -> + {ok, Req, State}; + +websocket_info(Info, Req, State) -> + io:format("Unhandled msg to ~p ~p\n", [?MODULE, Info]), + {ok, Req, State}. + +websocket_terminate(_Reason, _Req, _State) -> ok. + +websocket_handle(_Msg, Req, State) -> + {ok, Req, State}. + diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl new file mode 100644 index 0000000..b21d476 --- /dev/null +++ b/src/md_amqp_subscriber.erl @@ -0,0 +1,245 @@ +%%%------------------------------------------------------------------- +%%% @author Jack Tang +%%% @copyright (C) 2013, Jack Tang +%%% @doc +%%% +%%% @end +%%% Created : 30 Dec 2013 by Jack Tang +%%%------------------------------------------------------------------- +-module(md_amqp_subscriber). + +-behaviour(gen_server). + +%% API +-export([start_link/2]). + +%% gen_server callbacks +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). + +-define(SERVER, ?MODULE). +-include_lib("amqp_client/include/amqp_client.hrl"). + + +-record(state, {node_sub_count = dict:new(), %{key:Node, value:Count} + node_sub_detail = dict:new()}). %{key:InvestorId, value:{Node, Time}} + +%%%=================================================================== +%%% API +%%%=================================================================== + +%%-------------------------------------------------------------------- +%% @doc +%% Starts the server +%% +%% @spec start_link() -> {ok, Pid} | ignore | {error, Error} +%% @end +%%-------------------------------------------------------------------- +start_link(RoutingKey, Params) when is_binary(RoutingKey) -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [RoutingKey, Params], []); + +start_link(_RoutingKey, _Params) -> + io:format("RoutingKey should be binary type"). +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Initializes the server +%% +%% @spec init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% @end +%%-------------------------------------------------------------------- +init([RoutingKey, Params]) -> + Exchange = config_val(exchange, Params, list_to_binary(atom_to_list(?MODULE))), + AmqpParams = #amqp_params_network { + username = config_val(amqp_user, Params, <<"guest">>), + password = config_val(amqp_pass, Params, <<"guest">>), + virtual_host = config_val(amqp_vhost, Params, <<"/">>), + host = config_val(amqp_host, Params, "localhost"), + port = config_val(amqp_port, Params, 5672) + }, + + {ok, Channel} = amqp_channel(AmqpParams), + + #'exchange.declare_ok'{} = amqp_channel:call(Channel, #'exchange.declare'{ exchange = Exchange, + type = <<"topic">> }), + + %% Declare a queue + #'queue.declare_ok'{queue = Q} = amqp_channel:call(Channel, #'queue.declare'{}), + Binding = #'queue.bind'{queue = Q, exchange = Exchange, routing_key = RoutingKey}, + #'queue.bind_ok'{} = amqp_channel:call(Channel, Binding), + Sub = #'basic.consume'{queue = Q}, + % Subscribe the channel and consume the message + Consumer = self(), + #'basic.consume_ok'{} = amqp_channel:subscribe(Channel, Sub, Consumer), + + {ok, #state{}}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Handling call messages +%% +%% @spec handle_call(Request, From, State) -> +%% {reply, Reply, State} | +%% {reply, Reply, State, Timeout} | +%% {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, Reply, State} | +%% {stop, Reason, State} +%% @end +%%-------------------------------------------------------------------- +handle_call(_Request, _From, State) -> + lager:warning("Can't handle request: ~p", [_Request]), + {reply, {error, invalid_req}, State}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Handling cast messages +%% +%% @spec handle_cast(Msg, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% @end +%%-------------------------------------------------------------------- +handle_cast(_Msg, State) -> + lager:warning("Can't handle msg: ~p", [_Msg]), + {noreply, State}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Handling all non call/cast messages +%% +%% @spec handle_info(Info, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% @end +%%-------------------------------------------------------------------- +handle_info(#'basic.consume_ok'{}, State) -> + {noreply, State}; +handle_info(#'basic.cancel_ok'{}, State) -> + {noreply, State}; + +handle_info({#'basic.deliver'{delivery_tag = _Tag}, {_, _, Message} = _Content}, #state{} = State) -> + #state{node_sub_count = NodeSubCount, + node_sub_detail = NodeSubDetail} = State, + io:format("Message is ~p~n", [Message]), + {NodeSubCount0, NodeSubDetail0} = + case Message of + {connected, Login, Node, Time} -> + NodeSubCount1 = + case dict:find(Node, NodeSubCount) of + false -> + dict:store(Node, 1, NodeSubCount); + {ok, Value} -> + dict:store(Node, Value + 1, NodeSubCount) + end, + NodeSubDetail1 = + case dict:find(Login, NodeSubDetail) of + false -> + dict:store(Login, {Node, Time}, NodeSubDetail); + {ok, _Value1} -> + dict:store(Login, {Node, Time}, NodeSubDetail) + end, + {NodeSubCount1, NodeSubDetail1}; + {disconnected, Login, Node, _Time} -> + NodeSubCount1 = + case dict:find(Node, NodeSubCount) of + false -> + dict:store(Node, 0, NodeSubCount); + {ok, Value} -> + dict:store(Node, Value - 1, NodeSubCount) + end, + NodeSubDetail1 = + case dict:find(Login, NodeSubDetail) of + false -> + ok; + {ok, _Value1} -> + dict:erase(Login, NodeSubDetail) + end, + {NodeSubCount1, NodeSubDetail1} + end, + Msg={market_dispather, {NodeSubCount0, NodeSubDetail0}}, + bigwig_pubsubhub:notify(Msg), + {noreply, State#state{node_sub_count = NodeSubCount0, + node_sub_detail = NodeSubDetail0}}; + +handle_info(_Info, State) -> + lager:warning("Can't handle info: ~p", [_Info]), + {noreply, State}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any +%% necessary cleaning up. When it returns, the gen_server terminates +%% with Reason. The return value is ignored. +%% +%% @spec terminate(Reason, State) -> void() +%% @end +%%-------------------------------------------------------------------- +terminate(_Reason, _State) -> + ok. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Convert process state when code is changed +%% +%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} +%% @end +%%-------------------------------------------------------------------- +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + + +amqp_channel(AmqpParams) -> + case maybe_new_pid({AmqpParams, connection}, + fun() -> amqp_connection:start(AmqpParams) end) of + {ok, Client} -> + maybe_new_pid({AmqpParams, channel}, + fun() -> amqp_connection:open_channel(Client) end); + Error -> + Error + end. + +maybe_new_pid(Group, StartFun) -> + case pg2:get_closest_pid(Group) of + {error, {no_such_group, _}} -> + pg2:create(Group), + maybe_new_pid(Group, StartFun); + {error, {no_process, _}} -> + case StartFun() of + {ok, Pid} -> + pg2:join(Group, Pid), + {ok, Pid}; + Error -> + Error + end; + Pid -> + {ok, Pid} + end. + + +config_val(C, Params, Default) -> + case lists:keyfind(C, 1, Params) of + {C, V} -> V; + _ -> Default + end. From 18eafe229c24546f65924e25bb999e2d2b094f70 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 30 Dec 2013 18:22:43 +0800 Subject: [PATCH 360/405] #5200 --- src/bigwig_http_md_stream.erl | 2 +- src/md_amqp_subscriber.erl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl index cd5ee9a..f31dd8a 100644 --- a/src/bigwig_http_md_stream.erl +++ b/src/bigwig_http_md_stream.erl @@ -24,7 +24,7 @@ websocket_init(_TransportName, Req, _Opts) -> websocket_info({bigwig, {market_dispather, {NodeSubCount, _NodeSubDetail}}}, Req, State) -> NodeSubCountList = dict:to_list(NodeSubCount), - io:format("NodeSubCount is ~p ~n", [NodeSubCountList]), + io:format("NodeSubCount is ~p~n", [NodeSubCountList]), Reply = jsx:term_to_json([{market, NodeSubCountList}]), {reply, {text, Reply}, Req, State}; diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index b21d476..917edc7 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -100,7 +100,7 @@ init([RoutingKey, Params]) -> %% @end %%-------------------------------------------------------------------- handle_call(_Request, _From, State) -> - lager:warning("Can't handle request: ~p", [_Request]), + lager:warning("Can't handle request: ~p~n", [_Request]), {reply, {error, invalid_req}, State}. %%-------------------------------------------------------------------- @@ -114,7 +114,7 @@ handle_call(_Request, _From, State) -> %% @end %%-------------------------------------------------------------------- handle_cast(_Msg, State) -> - lager:warning("Can't handle msg: ~p", [_Msg]), + lager:warning("Can't handle msg: ~p~n", [_Msg]), {noreply, State}. %%-------------------------------------------------------------------- @@ -177,7 +177,7 @@ handle_info({#'basic.deliver'{delivery_tag = _Tag}, {_, _, Message} = _Content}, node_sub_detail = NodeSubDetail0}}; handle_info(_Info, State) -> - lager:warning("Can't handle info: ~p", [_Info]), + lager:warning("Can't handle info: ~p~n", [_Info]), {noreply, State}. %%-------------------------------------------------------------------- From 5e285a611b4415e21d15c2b0432a49806eec02e6 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 30 Dec 2013 18:27:23 +0800 Subject: [PATCH 361/405] #5200 --- src/bigwig_http_md_stream.erl | 2 +- src/md_amqp_subscriber.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl index f31dd8a..4fd3215 100644 --- a/src/bigwig_http_md_stream.erl +++ b/src/bigwig_http_md_stream.erl @@ -22,7 +22,7 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too -websocket_info({bigwig, {market_dispather, {NodeSubCount, _NodeSubDetail}}}, Req, State) -> +websocket_info({bigwig, {market_dispatcher, {NodeSubCount, _NodeSubDetail}}}, Req, State) -> NodeSubCountList = dict:to_list(NodeSubCount), io:format("NodeSubCount is ~p~n", [NodeSubCountList]), Reply = jsx:term_to_json([{market, NodeSubCountList}]), diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index 917edc7..a5007fc 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -171,7 +171,7 @@ handle_info({#'basic.deliver'{delivery_tag = _Tag}, {_, _, Message} = _Content}, end, {NodeSubCount1, NodeSubDetail1} end, - Msg={market_dispather, {NodeSubCount0, NodeSubDetail0}}, + Msg={market_dispatcher, {NodeSubCount0, NodeSubDetail0}}, bigwig_pubsubhub:notify(Msg), {noreply, State#state{node_sub_count = NodeSubCount0, node_sub_detail = NodeSubDetail0}}; From 242d5ca6baea50b93d2c822738530f29db56dd73 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Mon, 30 Dec 2013 18:31:59 +0800 Subject: [PATCH 362/405] #5200 --- src/bigwig_http_md_stream.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl index 4fd3215..3111df7 100644 --- a/src/bigwig_http_md_stream.erl +++ b/src/bigwig_http_md_stream.erl @@ -1,4 +1,4 @@ -%% Websocket connection that sends log of trace +%% Websocket connection that sends node_sub_count and node_sub_detail of market_dispatcher node %% -module(bigwig_http_md_stream). -behaviour(cowboy_websocket_handler). From 15f4ffc0154dabdc1ca38ba57f2b4c5f66db3b61 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Tue, 31 Dec 2013 13:30:40 +0800 Subject: [PATCH 363/405] #5200 --- src/bigwig_http_md_stream.erl | 8 ++++---- src/md_amqp_subscriber.erl | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl index 3111df7..a8dd46c 100644 --- a/src/bigwig_http_md_stream.erl +++ b/src/bigwig_http_md_stream.erl @@ -22,10 +22,10 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too -websocket_info({bigwig, {market_dispatcher, {NodeSubCount, _NodeSubDetail}}}, Req, State) -> - NodeSubCountList = dict:to_list(NodeSubCount), - io:format("NodeSubCount is ~p~n", [NodeSubCountList]), - Reply = jsx:term_to_json([{market, NodeSubCountList}]), +websocket_info({bigwig, {market_dispatcher, {NodeSubCount, NodeSubDetail}}}, Req, State) -> + io:format("NodeSubCount is ~p~n", [NodeSubCount]), + io:format("NodeSubDetail is ~p~n", [NodeSubDetail]), + Reply = jsx:term_to_json([{market, NodeSubCount, NodeSubDetail}]), {reply, {text, Reply}, Req, State}; diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index a5007fc..c96cc6a 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -171,7 +171,7 @@ handle_info({#'basic.deliver'{delivery_tag = _Tag}, {_, _, Message} = _Content}, end, {NodeSubCount1, NodeSubDetail1} end, - Msg={market_dispatcher, {NodeSubCount0, NodeSubDetail0}}, + Msg={market_dispatcher, {dict:to_list(NodeSubCount0), dict:to_list(NodeSubDetail0)}}, bigwig_pubsubhub:notify(Msg), {noreply, State#state{node_sub_count = NodeSubCount0, node_sub_detail = NodeSubDetail0}}; From 31a1dae7352d4349e5302b3f640cfb661465b68d Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Tue, 31 Dec 2013 14:19:52 +0800 Subject: [PATCH 364/405] #5200 --- src/bigwig_http_md_stream.erl | 6 +-- src/md_amqp_subscriber.erl | 91 ++++++++++++++++++----------------- 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl index a8dd46c..940252d 100644 --- a/src/bigwig_http_md_stream.erl +++ b/src/bigwig_http_md_stream.erl @@ -23,8 +23,8 @@ websocket_init(_TransportName, Req, _Opts) -> %% TODO handle stuff like {bigwig, {appmon, ... }} and send that too websocket_info({bigwig, {market_dispatcher, {NodeSubCount, NodeSubDetail}}}, Req, State) -> - io:format("NodeSubCount is ~p~n", [NodeSubCount]), - io:format("NodeSubDetail is ~p~n", [NodeSubDetail]), + lager:debug("NodeSubCount is ~p~n", [NodeSubCount]), + lager:debug("NodeSubDetail is ~p~n", [NodeSubDetail]), Reply = jsx:term_to_json([{market, NodeSubCount, NodeSubDetail}]), {reply, {text, Reply}, Req, State}; @@ -33,7 +33,7 @@ websocket_info({bigwig, _}, Req, State) -> {ok, Req, State}; websocket_info(Info, Req, State) -> - io:format("Unhandled msg to ~p ~p\n", [?MODULE, Info]), + lager:debug("Unhandled msg to ~p ~p\n", [?MODULE, Info]), {ok, Req, State}. websocket_terminate(_Reason, _Req, _State) -> ok. diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index c96cc6a..46c4df5 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -25,8 +25,8 @@ -include_lib("amqp_client/include/amqp_client.hrl"). --record(state, {node_sub_count = dict:new(), %{key:Node, value:Count} - node_sub_detail = dict:new()}). %{key:InvestorId, value:{Node, Time}} +-record(state, {node_sub_count, %orddict {key:Node, value:Count} + node_sub_detail}). %orddict {key:InvestorId, value:{Node, Time}} %%%=================================================================== %%% API @@ -83,7 +83,8 @@ init([RoutingKey, Params]) -> Consumer = self(), #'basic.consume_ok'{} = amqp_channel:subscribe(Channel, Sub, Consumer), - {ok, #state{}}. + {ok, #state{node_sub_count = orddict:new(), %orddict {key:Node, value:Count} + node_sub_detail = orddict:new()}}. %%-------------------------------------------------------------------- %% @private @@ -132,50 +133,54 @@ handle_info(#'basic.consume_ok'{}, State) -> handle_info(#'basic.cancel_ok'{}, State) -> {noreply, State}; -handle_info({#'basic.deliver'{delivery_tag = _Tag}, {_, _, Message} = _Content}, #state{} = State) -> +handle_info({#'basic.deliver'{delivery_tag = _Tag}, + {_, _, {connected, Login, Node, Time} = Message} = _Content}, #state{} = State) -> #state{node_sub_count = NodeSubCount, node_sub_detail = NodeSubDetail} = State, - io:format("Message is ~p~n", [Message]), - {NodeSubCount0, NodeSubDetail0} = - case Message of - {connected, Login, Node, Time} -> - NodeSubCount1 = - case dict:find(Node, NodeSubCount) of - false -> - dict:store(Node, 1, NodeSubCount); - {ok, Value} -> - dict:store(Node, Value + 1, NodeSubCount) - end, - NodeSubDetail1 = - case dict:find(Login, NodeSubDetail) of - false -> - dict:store(Login, {Node, Time}, NodeSubDetail); - {ok, _Value1} -> - dict:store(Login, {Node, Time}, NodeSubDetail) - end, - {NodeSubCount1, NodeSubDetail1}; - {disconnected, Login, Node, _Time} -> - NodeSubCount1 = - case dict:find(Node, NodeSubCount) of - false -> - dict:store(Node, 0, NodeSubCount); - {ok, Value} -> - dict:store(Node, Value - 1, NodeSubCount) - end, - NodeSubDetail1 = - case dict:find(Login, NodeSubDetail) of - false -> - ok; - {ok, _Value1} -> - dict:erase(Login, NodeSubDetail) - end, - {NodeSubCount1, NodeSubDetail1} - end, - Msg={market_dispatcher, {dict:to_list(NodeSubCount0), dict:to_list(NodeSubDetail0)}}, + lager:debug("Message is ~p~n", [Message]), + NodeSubCount1 = + case dict:find(Node, NodeSubCount) of + false -> + dict:store(Node, 1, NodeSubCount); + {ok, Value} -> + dict:store(Node, Value + 1, NodeSubCount) + end, + NodeSubDetail1 = + case dict:find(Login, NodeSubDetail) of + false -> + dict:store(Login, {Node, Time}, NodeSubDetail); + {ok, _Value1} -> + dict:store(Login, {Node, Time}, NodeSubDetail) + end, + Msg={market_dispatcher, {dict:to_list(NodeSubCount1), dict:to_list(NodeSubDetail1)}}, bigwig_pubsubhub:notify(Msg), - {noreply, State#state{node_sub_count = NodeSubCount0, - node_sub_detail = NodeSubDetail0}}; + {noreply, State#state{node_sub_count = NodeSubCount1, + node_sub_detail = NodeSubDetail1}}; + +handle_info({#'basic.deliver'{delivery_tag = _Tag}, + {_, _, {disconnected, Login, Node, Time} = Message} = _Content}, #state{} = State) -> + #state{node_sub_count = NodeSubCount, + node_sub_detail = NodeSubDetail} = State, + lager:debug("Message is ~p~n", [Message]), + NodeSubCount1 = + case dict:find(Node, NodeSubCount) of + false -> + dict:store(Node, 0, NodeSubCount); + {ok, Value} -> + dict:store(Node, Value - 1, NodeSubCount) + end, + NodeSubDetail1 = + case dict:find(Login, NodeSubDetail) of + false -> + ok; + {ok, _Value1} -> + dict:erase(Login, NodeSubDetail) + end, + Msg={market_dispatcher, {dict:to_list(NodeSubCount1), dict:to_list(NodeSubDetail1)}}, + bigwig_pubsubhub:notify(Msg), + {noreply, State#state{node_sub_count = NodeSubCount1, + node_sub_detail = NodeSubDetail1}}; handle_info(_Info, State) -> lager:warning("Can't handle info: ~p~n", [_Info]), {noreply, State}. From 762dcbe97035eab3131cdd319bf9e1a00517fac8 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Tue, 31 Dec 2013 14:28:00 +0800 Subject: [PATCH 365/405] #5200 --- src/md_amqp_subscriber.erl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index 46c4df5..c8e37d4 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -84,7 +84,7 @@ init([RoutingKey, Params]) -> #'basic.consume_ok'{} = amqp_channel:subscribe(Channel, Sub, Consumer), {ok, #state{node_sub_count = orddict:new(), %orddict {key:Node, value:Count} - node_sub_detail = orddict:new()}}. + node_sub_detail = orddict:new()}}. %orddict{key:Uid, value:{Node, Time}} %%-------------------------------------------------------------------- %% @private @@ -139,24 +139,24 @@ handle_info({#'basic.deliver'{delivery_tag = _Tag}, node_sub_detail = NodeSubDetail} = State, lager:debug("Message is ~p~n", [Message]), NodeSubCount1 = - case dict:find(Node, NodeSubCount) of + case orddict:find(Node, NodeSubCount) of false -> - dict:store(Node, 1, NodeSubCount); + orddict:store(Node, 1, NodeSubCount); {ok, Value} -> - dict:store(Node, Value + 1, NodeSubCount) + orddict:store(Node, Value + 1, NodeSubCount) end, NodeSubDetail1 = - case dict:find(Login, NodeSubDetail) of + case orddict:find(Login, NodeSubDetail) of false -> - dict:store(Login, {Node, Time}, NodeSubDetail); + orddict:store(Login, {Node, Time}, NodeSubDetail); {ok, _Value1} -> - dict:store(Login, {Node, Time}, NodeSubDetail) + orddict:store(Login, {Node, Time}, NodeSubDetail) end, - Msg={market_dispatcher, {dict:to_list(NodeSubCount1), dict:to_list(NodeSubDetail1)}}, + Msg={market_dispatcher, {orddict:to_list(NodeSubCount1), orddict:to_list(NodeSubDetail1)}}, bigwig_pubsubhub:notify(Msg), {noreply, State#state{node_sub_count = NodeSubCount1, node_sub_detail = NodeSubDetail1}}; - + handle_info({#'basic.deliver'{delivery_tag = _Tag}, {_, _, {disconnected, Login, Node, Time} = Message} = _Content}, #state{} = State) -> @@ -164,20 +164,20 @@ handle_info({#'basic.deliver'{delivery_tag = _Tag}, node_sub_detail = NodeSubDetail} = State, lager:debug("Message is ~p~n", [Message]), NodeSubCount1 = - case dict:find(Node, NodeSubCount) of + case orddict:find(Node, NodeSubCount) of false -> - dict:store(Node, 0, NodeSubCount); + orddict:store(Node, 0, NodeSubCount); {ok, Value} -> - dict:store(Node, Value - 1, NodeSubCount) + orddict:store(Node, Value - 1, NodeSubCount) end, NodeSubDetail1 = - case dict:find(Login, NodeSubDetail) of + case orddict:find(Login, NodeSubDetail) of false -> ok; {ok, _Value1} -> - dict:erase(Login, NodeSubDetail) + orddict:erase(Login, NodeSubDetail) end, - Msg={market_dispatcher, {dict:to_list(NodeSubCount1), dict:to_list(NodeSubDetail1)}}, + Msg={market_dispatcher, {orddict:to_list(NodeSubCount1), orddict:to_list(NodeSubDetail1)}}, bigwig_pubsubhub:notify(Msg), {noreply, State#state{node_sub_count = NodeSubCount1, node_sub_detail = NodeSubDetail1}}; From 2a73bcba0c93a3ba7e88b9fbe4230ad35d91ad58 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Tue, 31 Dec 2013 14:40:33 +0800 Subject: [PATCH 366/405] #5200 --- src/md_amqp_subscriber.erl | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index c8e37d4..161a4ad 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -11,7 +11,7 @@ -behaviour(gen_server). %% API --export([start_link/2]). +-export([start_link/1]). %% gen_server callbacks -export([init/1, @@ -39,11 +39,9 @@ %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} %% @end %%-------------------------------------------------------------------- -start_link(RoutingKey, Params) when is_binary(RoutingKey) -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [RoutingKey, Params], []); +start_link(Params) -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [Params], []). -start_link(_RoutingKey, _Params) -> - io:format("RoutingKey should be binary type"). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== @@ -59,7 +57,7 @@ start_link(_RoutingKey, _Params) -> %% {stop, Reason} %% @end %%-------------------------------------------------------------------- -init([RoutingKey, Params]) -> +init([Params]) -> Exchange = config_val(exchange, Params, list_to_binary(atom_to_list(?MODULE))), AmqpParams = #amqp_params_network { username = config_val(amqp_user, Params, <<"guest">>), @@ -76,7 +74,7 @@ init([RoutingKey, Params]) -> %% Declare a queue #'queue.declare_ok'{queue = Q} = amqp_channel:call(Channel, #'queue.declare'{}), - Binding = #'queue.bind'{queue = Q, exchange = Exchange, routing_key = RoutingKey}, + Binding = #'queue.bind'{queue = Q, exchange = Exchange, routing_key = <<"mdstatistic">>}, #'queue.bind_ok'{} = amqp_channel:call(Channel, Binding), Sub = #'basic.consume'{queue = Q}, % Subscribe the channel and consume the message @@ -159,7 +157,7 @@ handle_info({#'basic.deliver'{delivery_tag = _Tag}, handle_info({#'basic.deliver'{delivery_tag = _Tag}, - {_, _, {disconnected, Login, Node, Time} = Message} = _Content}, #state{} = State) -> + {_, _, {disconnected, Login, Node, _Time} = Message} = _Content}, #state{} = State) -> #state{node_sub_count = NodeSubCount, node_sub_detail = NodeSubDetail} = State, lager:debug("Message is ~p~n", [Message]), From c4f60236710abe39d49ab1bd69f0b1b9b81c6fa9 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 09:50:17 +0800 Subject: [PATCH 367/405] #5200 --- src/md_amqp_subscriber.erl | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index 161a4ad..007906c 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -132,28 +132,28 @@ handle_info(#'basic.cancel_ok'{}, State) -> {noreply, State}; handle_info({#'basic.deliver'{delivery_tag = _Tag}, - {_, _, {connected, Login, Node, Time} = Message} = _Content}, #state{} = State) -> + {_, _, Message} = _Content}, #state{} = State) -> #state{node_sub_count = NodeSubCount, node_sub_detail = NodeSubDetail} = State, lager:debug("Message is ~p~n", [Message]), - NodeSubCount1 = - case orddict:find(Node, NodeSubCount) of - false -> - orddict:store(Node, 1, NodeSubCount); - {ok, Value} -> - orddict:store(Node, Value + 1, NodeSubCount) - end, - NodeSubDetail1 = - case orddict:find(Login, NodeSubDetail) of - false -> - orddict:store(Login, {Node, Time}, NodeSubDetail); - {ok, _Value1} -> - orddict:store(Login, {Node, Time}, NodeSubDetail) - end, - Msg={market_dispatcher, {orddict:to_list(NodeSubCount1), orddict:to_list(NodeSubDetail1)}}, - bigwig_pubsubhub:notify(Msg), - {noreply, State#state{node_sub_count = NodeSubCount1, - node_sub_detail = NodeSubDetail1}}; +% NodeSubCount1 = +% case orddict:find(Node, NodeSubCount) of +% false -> +% orddict:store(Node, 1, NodeSubCount); +% {ok, Value} -> +% orddict:store(Node, Value + 1, NodeSubCount) +% end, +% NodeSubDetail1 = +% case orddict:find(Login, NodeSubDetail) of +% false -> +% orddict:store(Login, {Node, Time}, NodeSubDetail); +% {ok, _Value1} -> +% orddict:store(Login, {Node, Time}, NodeSubDetail) +% end, +% Msg={market_dispatcher, {orddict:to_list(NodeSubCount1), orddict:to_list(NodeSubDetail1)}}, +% bigwig_pubsubhub:notify(Msg), + {noreply, State#state{node_sub_count = NodeSubCount, + node_sub_detail = NodeSubDetail}}; handle_info({#'basic.deliver'{delivery_tag = _Tag}, From 1a2bd03f73ee891620cdf8112facd3b7ffe723dd Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 13:48:36 +0800 Subject: [PATCH 368/405] #5200 --- priv/html/index.html | 4 ++ priv/js/market.js | 23 ++++++++++ src/bigwig_app.erl | 1 + src/bigwig_http_md_stream.erl | 2 +- src/md_amqp_subscriber.erl | 86 ++++++++++++++++++----------------- 5 files changed, 73 insertions(+), 43 deletions(-) create mode 100644 priv/js/market.js diff --git a/priv/html/index.html b/priv/html/index.html index 131630f..6250e4a 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -19,6 +19,7 @@

    BigWig

  • Applications
  • Lager Logging
  • Entop
  • +
  • Market Subscriber Statistic
  • @@ -156,6 +157,9 @@

    BigWig

  • +
    +
    +
    Tracer: diff --git a/priv/js/market.js b/priv/js/market.js new file mode 100644 index 0000000..2e4bb78 --- /dev/null +++ b/priv/js/market.js @@ -0,0 +1,23 @@ +$(document).ready(function(){ + connect("/market/stream"); +}); + +function connect(to) +{ + var host = document.location.host; + websocket = new WebSocket("ws://"+host+to); + websocket.onopen = function(evt) { onOpen(evt) }; + websocket.onclose = function(evt) { onClose(evt) }; + websocket.onmessage = function(evt) { onMessage(evt) }; +}; + +function onOpen(evt) { +}; + +function onClose(evt) { +}; + +function onMessage(evt) { + $('#market_statistic').prepend('

    ' + evt.data + '

    '); +}; + diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index 4f4c979..d2ad727 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -28,6 +28,7 @@ start(_StartType, _StartArgs) -> {"/stats-stream", bigwig_http_stats_stream, []}, {"/lager/stream", bigwig_http_lager_stream, []}, {"/lager/[...]", bigwig_http_lager, []}, + {"/market/stream", bigwig_http_md_stream, []}, {'_', bigwig_http_catchall, []} ]} ]), diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl index 940252d..9545932 100644 --- a/src/bigwig_http_md_stream.erl +++ b/src/bigwig_http_md_stream.erl @@ -25,7 +25,7 @@ websocket_init(_TransportName, Req, _Opts) -> websocket_info({bigwig, {market_dispatcher, {NodeSubCount, NodeSubDetail}}}, Req, State) -> lager:debug("NodeSubCount is ~p~n", [NodeSubCount]), lager:debug("NodeSubDetail is ~p~n", [NodeSubDetail]), - Reply = jsx:term_to_json([{market, NodeSubCount, NodeSubDetail}]), + Reply = jsx:term_to_json([{market, NodeSubCount}]), {reply, {text, Reply}, Req, State}; diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index 007906c..bfe5b22 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -58,7 +58,7 @@ start_link(Params) -> %% @end %%-------------------------------------------------------------------- init([Params]) -> - Exchange = config_val(exchange, Params, list_to_binary(atom_to_list(?MODULE))), + Exchange = config_val(exchange, Params, <<"market_subscriber_statistic">>), AmqpParams = #amqp_params_network { username = config_val(amqp_user, Params, <<"guest">>), password = config_val(amqp_pass, Params, <<"guest">>), @@ -136,49 +136,51 @@ handle_info({#'basic.deliver'{delivery_tag = _Tag}, #state{node_sub_count = NodeSubCount, node_sub_detail = NodeSubDetail} = State, lager:debug("Message is ~p~n", [Message]), -% NodeSubCount1 = -% case orddict:find(Node, NodeSubCount) of -% false -> -% orddict:store(Node, 1, NodeSubCount); -% {ok, Value} -> -% orddict:store(Node, Value + 1, NodeSubCount) -% end, -% NodeSubDetail1 = -% case orddict:find(Login, NodeSubDetail) of -% false -> -% orddict:store(Login, {Node, Time}, NodeSubDetail); -% {ok, _Value1} -> -% orddict:store(Login, {Node, Time}, NodeSubDetail) -% end, -% Msg={market_dispatcher, {orddict:to_list(NodeSubCount1), orddict:to_list(NodeSubDetail1)}}, -% bigwig_pubsubhub:notify(Msg), - {noreply, State#state{node_sub_count = NodeSubCount, - node_sub_detail = NodeSubDetail}}; - - -handle_info({#'basic.deliver'{delivery_tag = _Tag}, - {_, _, {disconnected, Login, Node, _Time} = Message} = _Content}, #state{} = State) -> - #state{node_sub_count = NodeSubCount, - node_sub_detail = NodeSubDetail} = State, - lager:debug("Message is ~p~n", [Message]), - NodeSubCount1 = - case orddict:find(Node, NodeSubCount) of - false -> - orddict:store(Node, 0, NodeSubCount); - {ok, Value} -> - orddict:store(Node, Value - 1, NodeSubCount) + [Conn0, Login0, Node0, Time0] = binary:split(Message, <<" ">>, [global]), + Conn = binary_to_atom(Conn0, utf8), + Login = list_to_integer(binary_to_list(Login0)), + Node = binary_to_atom(Node0, utf8), + Time = binary_to_atom(Time0, utf8), + {NodeSubCount0, NodeSubDetail0} = + case Conn of + connected -> + NodeSubCount1 = + case orddict:find(Node, NodeSubCount) of + false -> + orddict:store(Node, 1, NodeSubCount); + {ok, Value} -> + orddict:store(Node, Value + 1, NodeSubCount) + end, + NodeSubDetail1 = + case orddict:find(Login, NodeSubDetail) of + false -> + orddict:store(Login, {Node, Time}, NodeSubDetail); + {ok, _Value1} -> + orddict:store(Login, {Node, Time}, NodeSubDetail) + end, + {NodeSubCount1, NodeSubDetail1}; + disconnected -> + NodeSubCount1 = + case orddict:find(Node, NodeSubCount) of + false -> + orddict:store(Node, 0, NodeSubCount); + {ok, Value} -> + orddict:store(Node, Value - 1, NodeSubCount) + end, + NodeSubDetail1 = + case orddict:find(Login, NodeSubDetail) of + false -> + ok; + {ok, _Value1} -> + orddict:erase(Login, NodeSubDetail) + end, + {NodeSubCount1, NodeSubDetail1} end, - NodeSubDetail1 = - case orddict:find(Login, NodeSubDetail) of - false -> - ok; - {ok, _Value1} -> - orddict:erase(Login, NodeSubDetail) - end, - Msg={market_dispatcher, {orddict:to_list(NodeSubCount1), orddict:to_list(NodeSubDetail1)}}, + Msg={market_dispatcher, {orddict:to_list(NodeSubCount0), orddict:to_list(NodeSubDetail0)}}, bigwig_pubsubhub:notify(Msg), - {noreply, State#state{node_sub_count = NodeSubCount1, - node_sub_detail = NodeSubDetail1}}; + {noreply, State#state{node_sub_count = NodeSubCount0, + node_sub_detail = NodeSubDetail0}}; + handle_info(_Info, State) -> lager:warning("Can't handle info: ~p~n", [_Info]), {noreply, State}. From dc8df8fa6af11fac3d13b46c16e4d58cea82177b Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 13:58:24 +0800 Subject: [PATCH 369/405] #5200 --- priv/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/html/index.html b/priv/html/index.html index 6250e4a..9269910 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -19,7 +19,7 @@

    BigWig

  • Applications
  • Lager Logging
  • Entop
  • -
  • Market Subscriber Statistic
  • +
  • Market
  • From 4bfea73ba491ecdbb80adc47c462a52c2209664e Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 14:05:33 +0800 Subject: [PATCH 370/405] #5200 --- src/md_amqp_subscriber.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index bfe5b22..dd8844e 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -146,14 +146,14 @@ handle_info({#'basic.deliver'{delivery_tag = _Tag}, connected -> NodeSubCount1 = case orddict:find(Node, NodeSubCount) of - false -> + error -> orddict:store(Node, 1, NodeSubCount); {ok, Value} -> orddict:store(Node, Value + 1, NodeSubCount) end, NodeSubDetail1 = case orddict:find(Login, NodeSubDetail) of - false -> + error -> orddict:store(Login, {Node, Time}, NodeSubDetail); {ok, _Value1} -> orddict:store(Login, {Node, Time}, NodeSubDetail) @@ -162,14 +162,14 @@ handle_info({#'basic.deliver'{delivery_tag = _Tag}, disconnected -> NodeSubCount1 = case orddict:find(Node, NodeSubCount) of - false -> + error -> orddict:store(Node, 0, NodeSubCount); {ok, Value} -> orddict:store(Node, Value - 1, NodeSubCount) end, NodeSubDetail1 = case orddict:find(Login, NodeSubDetail) of - false -> + error -> ok; {ok, _Value1} -> orddict:erase(Login, NodeSubDetail) From 19f3f901f23e5f740c0d141230829022aa6f6d31 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 14:59:29 +0800 Subject: [PATCH 371/405] #5200 --- priv/html/index.html | 5 +++-- priv/js/market.js | 7 ++++--- src/bigwig_app.erl | 2 +- src/bigwig_http_md_stream.erl | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index 9269910..3e9bbe1 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -157,8 +157,8 @@

    BigWig

  • -
    -
    +
    +
    @@ -227,5 +227,6 @@

    BigWig

    + diff --git a/priv/js/market.js b/priv/js/market.js index 2e4bb78..f69d861 100644 --- a/priv/js/market.js +++ b/priv/js/market.js @@ -1,5 +1,5 @@ $(document).ready(function(){ - connect("/market/stream"); + connect("/md/stream"); }); function connect(to) @@ -17,7 +17,8 @@ function onOpen(evt) { function onClose(evt) { }; -function onMessage(evt) { - $('#market_statistic').prepend('

    ' + evt.data + '

    '); +function onMessage(evt) { + document.getElementById("md_statistic").innerHTML=""; + $('#md_statistic').prepend('

    ' + evt.data + '

    '); }; diff --git a/src/bigwig_app.erl b/src/bigwig_app.erl index d2ad727..6ca2e22 100644 --- a/src/bigwig_app.erl +++ b/src/bigwig_app.erl @@ -28,7 +28,7 @@ start(_StartType, _StartArgs) -> {"/stats-stream", bigwig_http_stats_stream, []}, {"/lager/stream", bigwig_http_lager_stream, []}, {"/lager/[...]", bigwig_http_lager, []}, - {"/market/stream", bigwig_http_md_stream, []}, + {"/md/stream", bigwig_http_md_stream, []}, {'_', bigwig_http_catchall, []} ]} ]), diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl index 9545932..98502ac 100644 --- a/src/bigwig_http_md_stream.erl +++ b/src/bigwig_http_md_stream.erl @@ -25,7 +25,7 @@ websocket_init(_TransportName, Req, _Opts) -> websocket_info({bigwig, {market_dispatcher, {NodeSubCount, NodeSubDetail}}}, Req, State) -> lager:debug("NodeSubCount is ~p~n", [NodeSubCount]), lager:debug("NodeSubDetail is ~p~n", [NodeSubDetail]), - Reply = jsx:term_to_json([{market, NodeSubCount}]), + Reply = jsx:term_to_json([NodeSubCount]), {reply, {text, Reply}, Req, State}; From f4c8325c5d7b0f23d6327a30d2940f16bc685875 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 15:19:32 +0800 Subject: [PATCH 372/405] #5200 --- priv/html/index.html | 2 +- src/bigwig_http_md_stream.erl | 2 +- src/md_amqp_subscriber.erl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/priv/html/index.html b/priv/html/index.html index 3e9bbe1..0cc8c36 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -19,7 +19,7 @@

    BigWig

  • Applications
  • Lager Logging
  • Entop
  • -
  • Market
  • +
  • Md
  • diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl index 98502ac..9545932 100644 --- a/src/bigwig_http_md_stream.erl +++ b/src/bigwig_http_md_stream.erl @@ -25,7 +25,7 @@ websocket_init(_TransportName, Req, _Opts) -> websocket_info({bigwig, {market_dispatcher, {NodeSubCount, NodeSubDetail}}}, Req, State) -> lager:debug("NodeSubCount is ~p~n", [NodeSubCount]), lager:debug("NodeSubDetail is ~p~n", [NodeSubDetail]), - Reply = jsx:term_to_json([NodeSubCount]), + Reply = jsx:term_to_json([{market, NodeSubCount}]), {reply, {text, Reply}, Req, State}; diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index dd8844e..1eed954 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -74,7 +74,7 @@ init([Params]) -> %% Declare a queue #'queue.declare_ok'{queue = Q} = amqp_channel:call(Channel, #'queue.declare'{}), - Binding = #'queue.bind'{queue = Q, exchange = Exchange, routing_key = <<"mdstatistic">>}, + Binding = #'queue.bind'{queue = Q, exchange = Exchange, routing_key = <<"md_stat">>}, #'queue.bind_ok'{} = amqp_channel:call(Channel, Binding), Sub = #'basic.consume'{queue = Q}, % Subscribe the channel and consume the message From 9d77b8de6c85363283a19c555f1faf7e2bde9b82 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 15:23:44 +0800 Subject: [PATCH 373/405] #5200 --- src/bigwig_http_md_stream.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl index 9545932..98502ac 100644 --- a/src/bigwig_http_md_stream.erl +++ b/src/bigwig_http_md_stream.erl @@ -25,7 +25,7 @@ websocket_init(_TransportName, Req, _Opts) -> websocket_info({bigwig, {market_dispatcher, {NodeSubCount, NodeSubDetail}}}, Req, State) -> lager:debug("NodeSubCount is ~p~n", [NodeSubCount]), lager:debug("NodeSubDetail is ~p~n", [NodeSubDetail]), - Reply = jsx:term_to_json([{market, NodeSubCount}]), + Reply = jsx:term_to_json([NodeSubCount]), {reply, {text, Reply}, Req, State}; From 42ad6769c2ff657375b5b0875aa9d85e74083c4f Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 15:44:37 +0800 Subject: [PATCH 374/405] #5200 --- priv/js/market.js | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 priv/js/market.js diff --git a/priv/js/market.js b/priv/js/market.js deleted file mode 100644 index f69d861..0000000 --- a/priv/js/market.js +++ /dev/null @@ -1,24 +0,0 @@ -$(document).ready(function(){ - connect("/md/stream"); -}); - -function connect(to) -{ - var host = document.location.host; - websocket = new WebSocket("ws://"+host+to); - websocket.onopen = function(evt) { onOpen(evt) }; - websocket.onclose = function(evt) { onClose(evt) }; - websocket.onmessage = function(evt) { onMessage(evt) }; -}; - -function onOpen(evt) { -}; - -function onClose(evt) { -}; - -function onMessage(evt) { - document.getElementById("md_statistic").innerHTML=""; - $('#md_statistic').prepend('

    ' + evt.data + '

    '); -}; - From 2c29a469efc7860294e1c5da223e538f11b0e0fb Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 15:45:06 +0800 Subject: [PATCH 375/405] #5200 --- priv/html/index.html | 2 +- priv/js/md_stat.js | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 priv/js/md_stat.js diff --git a/priv/html/index.html b/priv/html/index.html index 0cc8c36..05b6b4b 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -227,6 +227,6 @@

    BigWig

    - + diff --git a/priv/js/md_stat.js b/priv/js/md_stat.js new file mode 100644 index 0000000..f69d861 --- /dev/null +++ b/priv/js/md_stat.js @@ -0,0 +1,24 @@ +$(document).ready(function(){ + connect("/md/stream"); +}); + +function connect(to) +{ + var host = document.location.host; + websocket = new WebSocket("ws://"+host+to); + websocket.onopen = function(evt) { onOpen(evt) }; + websocket.onclose = function(evt) { onClose(evt) }; + websocket.onmessage = function(evt) { onMessage(evt) }; +}; + +function onOpen(evt) { +}; + +function onClose(evt) { +}; + +function onMessage(evt) { + document.getElementById("md_statistic").innerHTML=""; + $('#md_statistic').prepend('

    ' + evt.data + '

    '); +}; + From c7f83203aaacf0802840455c8fd909fce57cb5fa Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 16:49:56 +0800 Subject: [PATCH 376/405] #5200 --- priv/js/md_stat.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/priv/js/md_stat.js b/priv/js/md_stat.js index f69d861..e38e097 100644 --- a/priv/js/md_stat.js +++ b/priv/js/md_stat.js @@ -19,6 +19,12 @@ function onClose(evt) { function onMessage(evt) { document.getElementById("md_statistic").innerHTML=""; - $('#md_statistic').prepend('

    ' + evt.data + '

    '); + var x = (evt.data).split("\""); + var x1 = x[1].split("\":"); + var x2 = x1[1].split("}"); + + $('#md_statistic').append('

    ' + "MDNode:" + x1[0] + '

    '); + $('#md_statistic').append('

    ' + "Count:" + x2[0] + '

    '); + }; From 27da30554435dcbd24845fa9c711f02d3bd3b4e2 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 17:26:41 +0800 Subject: [PATCH 377/405] #5500 --- priv/js/md_stat.js | 11 ++++++----- src/bigwig_http_md_stream.erl | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/priv/js/md_stat.js b/priv/js/md_stat.js index e38e097..ae5e46f 100644 --- a/priv/js/md_stat.js +++ b/priv/js/md_stat.js @@ -19,12 +19,13 @@ function onClose(evt) { function onMessage(evt) { document.getElementById("md_statistic").innerHTML=""; - var x = (evt.data).split("\""); - var x1 = x[1].split("\":"); - var x2 = x1[1].split("}"); - $('#md_statistic').append('

    ' + "MDNode:" + x1[0] + '

    '); - $('#md_statistic').append('

    ' + "Count:" + x2[0] + '

    '); + for(var key in evt.data) + { + $('#md_statistic').append('

    ' + "MDNode: " + key + '

    '); + $('#md_statistic').append('

    ' + "Count: " + evt.data(key) + '

    '); + } + }; diff --git a/src/bigwig_http_md_stream.erl b/src/bigwig_http_md_stream.erl index 98502ac..38e9442 100644 --- a/src/bigwig_http_md_stream.erl +++ b/src/bigwig_http_md_stream.erl @@ -25,7 +25,7 @@ websocket_init(_TransportName, Req, _Opts) -> websocket_info({bigwig, {market_dispatcher, {NodeSubCount, NodeSubDetail}}}, Req, State) -> lager:debug("NodeSubCount is ~p~n", [NodeSubCount]), lager:debug("NodeSubDetail is ~p~n", [NodeSubDetail]), - Reply = jsx:term_to_json([NodeSubCount]), + Reply = jsx:term_to_json(NodeSubCount), {reply, {text, Reply}, Req, State}; From 721bc916cc05d3109e87807b15a4b0a2f0615fae Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 17:34:01 +0800 Subject: [PATCH 378/405] #5200 --- priv/js/md_stat.js | 2 +- src/md_amqp_subscriber.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/priv/js/md_stat.js b/priv/js/md_stat.js index ae5e46f..0481997 100644 --- a/priv/js/md_stat.js +++ b/priv/js/md_stat.js @@ -19,7 +19,7 @@ function onClose(evt) { function onMessage(evt) { document.getElementById("md_statistic").innerHTML=""; - + alert(evt.data); for(var key in evt.data) { $('#md_statistic').append('

    ' + "MDNode: " + key + '

    '); diff --git a/src/md_amqp_subscriber.erl b/src/md_amqp_subscriber.erl index 1eed954..6a20159 100644 --- a/src/md_amqp_subscriber.erl +++ b/src/md_amqp_subscriber.erl @@ -170,7 +170,7 @@ handle_info({#'basic.deliver'{delivery_tag = _Tag}, NodeSubDetail1 = case orddict:find(Login, NodeSubDetail) of error -> - ok; + NodeSubDetail; {ok, _Value1} -> orddict:erase(Login, NodeSubDetail) end, From 3df95f8f5b9ab06b12372d1351511c5411a6f423 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Thu, 2 Jan 2014 18:57:16 +0800 Subject: [PATCH 379/405] #5200 --- priv/js/md_stat.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/priv/js/md_stat.js b/priv/js/md_stat.js index 0481997..8505d41 100644 --- a/priv/js/md_stat.js +++ b/priv/js/md_stat.js @@ -19,11 +19,12 @@ function onClose(evt) { function onMessage(evt) { document.getElementById("md_statistic").innerHTML=""; - alert(evt.data); - for(var key in evt.data) + var obj = evt.data; + for(var node in obj) { - $('#md_statistic').append('

    ' + "MDNode: " + key + '

    '); - $('#md_statistic').append('

    ' + "Count: " + evt.data(key) + '

    '); + console.log(obj); + $('#md_statistic').append('

    ' + "MDNode: " + node + '

    '); + $('#md_statistic').append('

    ' + "Count: " + obj[node] + '

    '); } From 1b652fdd5276266fd7211c25cd0edabe46b8f5c3 Mon Sep 17 00:00:00 2001 From: Zhaoren Date: Fri, 3 Jan 2014 11:56:57 +0800 Subject: [PATCH 380/405] #5200 --- priv/html/index.html | 4 + priv/images/flipCounter-medium.png | Bin 0 -> 5555 bytes priv/js/jquery.easing.1.3.js | 205 ++++++++ priv/js/jquery.flipCounter.1.2.js | 724 +++++++++++++++++++++++++++++ priv/js/md_stat.js | 41 +- 5 files changed, 968 insertions(+), 6 deletions(-) create mode 100644 priv/images/flipCounter-medium.png create mode 100644 priv/js/jquery.easing.1.3.js create mode 100755 priv/js/jquery.flipCounter.1.2.js diff --git a/priv/html/index.html b/priv/html/index.html index 05b6b4b..69e7142 100644 --- a/priv/html/index.html +++ b/priv/html/index.html @@ -158,7 +158,9 @@

    BigWig

  • +
    +
    @@ -219,6 +221,8 @@

    BigWig

    + + diff --git a/priv/images/flipCounter-medium.png b/priv/images/flipCounter-medium.png new file mode 100644 index 0000000000000000000000000000000000000000..f80f25b75d93ec018c957e3079566c25502a4999 GIT binary patch literal 5555 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