rails-profiler 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/builds/profiler.js +263 -3
- data/app/controllers/profiler/api/console_controller.rb +46 -0
- data/app/controllers/profiler/api/profiles_controller.rb +1 -1
- data/config/routes.rb +3 -0
- data/lib/profiler/collectors/console_collector.rb +57 -0
- data/lib/profiler/configuration.rb +2 -0
- data/lib/profiler/console_profiler.rb +102 -0
- data/lib/profiler/instrumentation/irb_instrumentation.rb +21 -0
- data/lib/profiler/railtie.rb +8 -0
- data/lib/profiler/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbaa1466083b900023ccc95cdf77675a4dc80b67618db16155756b3d62325690
|
|
4
|
+
data.tar.gz: 8084c6725e5427bc3cec40d58b47c01604fa30b5aee2def8b13ff21c67907b6a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4141c0e5cb65245f961be688edcb8d38ccd8a2db98a64fbfacc74fc50fa87aec180298f268978cbafaa6dc06751fc9cb17b57784f0c7f44a773489b3306a2d8f
|
|
7
|
+
data.tar.gz: 2b2b3139162749df458dc874c2e68be2e0daf96c8fae29054f809d34b4469babeac0c630f7846f8b1e59d1a0cbd4120d2bd99ba4971142a1498d171ade98042f
|
|
@@ -1644,7 +1644,7 @@
|
|
|
1644
1644
|
const params = new URLSearchParams(window.location.search);
|
|
1645
1645
|
const initialSection = () => {
|
|
1646
1646
|
const s3 = params.get("section");
|
|
1647
|
-
return s3 === "http" || s3 === "jobs" || s3 === "outbound" || s3 === "env" ? s3 : "http";
|
|
1647
|
+
return s3 === "http" || s3 === "jobs" || s3 === "console" || s3 === "outbound" || s3 === "env" ? s3 : "http";
|
|
1648
1648
|
};
|
|
1649
1649
|
const initialSort = () => {
|
|
1650
1650
|
const col = params.get("sort");
|
|
@@ -1663,12 +1663,18 @@
|
|
|
1663
1663
|
const [jobOffset, setJobOffset] = d2(0);
|
|
1664
1664
|
const [jobHasMore, setJobHasMore] = d2(false);
|
|
1665
1665
|
const [jobLoadingMore, setJobLoadingMore] = d2(false);
|
|
1666
|
+
const [consoles, setConsoles] = d2([]);
|
|
1667
|
+
const [consoleOffset, setConsoleOffset] = d2(0);
|
|
1668
|
+
const [consoleHasMore, setConsoleHasMore] = d2(false);
|
|
1669
|
+
const [consoleLoadingMore, setConsoleLoadingMore] = d2(false);
|
|
1666
1670
|
const [outboundRequests, setOutboundRequests] = d2([]);
|
|
1667
1671
|
const [loadingHttp, setLoadingHttp] = d2(initialSection() === "http");
|
|
1668
1672
|
const [loadingJobs, setLoadingJobs] = d2(initialSection() === "jobs");
|
|
1673
|
+
const [loadingConsole, setLoadingConsole] = d2(initialSection() === "console");
|
|
1669
1674
|
const [loadingOutbound, setLoadingOutbound] = d2(initialSection() === "outbound");
|
|
1670
1675
|
const [error, setError] = d2(null);
|
|
1671
1676
|
const [jobsError, setJobsError] = d2(null);
|
|
1677
|
+
const [consoleError, setConsoleError] = d2(null);
|
|
1672
1678
|
const [outboundError, setOutboundError] = d2(null);
|
|
1673
1679
|
const [envData, setEnvData] = d2(void 0);
|
|
1674
1680
|
const [loadingEnv, setLoadingEnv] = d2(initialSection() === "env");
|
|
@@ -1684,9 +1690,12 @@
|
|
|
1684
1690
|
});
|
|
1685
1691
|
const [httpSort, setHttpSort] = d2(initialSort);
|
|
1686
1692
|
const [jobSort, setJobSort] = d2({ col: null, dir: "asc" });
|
|
1693
|
+
const [consoleSort, setConsoleSort] = d2({ col: null, dir: "asc" });
|
|
1687
1694
|
const [jobSearch, setJobSearch] = d2("");
|
|
1688
1695
|
const [jobStatus, setJobStatus] = d2("");
|
|
1689
1696
|
const [jobDuration, setJobDuration] = d2("");
|
|
1697
|
+
const [consoleSearch, setConsoleSearch] = d2("");
|
|
1698
|
+
const [consoleStatus, setConsoleStatus] = d2("");
|
|
1690
1699
|
const [outboundSearch, setOutboundSearch] = d2("");
|
|
1691
1700
|
const [outboundMethod, setOutboundMethod] = d2("");
|
|
1692
1701
|
const [outboundStatus, setOutboundStatus] = d2("");
|
|
@@ -1740,6 +1749,15 @@
|
|
|
1740
1749
|
setJobLoadingMore(false);
|
|
1741
1750
|
}).catch(() => setJobLoadingMore(false));
|
|
1742
1751
|
};
|
|
1752
|
+
const loadMoreConsole = () => {
|
|
1753
|
+
setConsoleLoadingMore(true);
|
|
1754
|
+
fetch(`${BASE}/api/console?limit=50&offset=${consoleOffset}`).then((res) => res.json()).then((data) => {
|
|
1755
|
+
setConsoles((prev) => [...prev, ...data.profiles]);
|
|
1756
|
+
setConsoleOffset((prev) => prev + data.profiles.length);
|
|
1757
|
+
setConsoleHasMore(data.has_more);
|
|
1758
|
+
setConsoleLoadingMore(false);
|
|
1759
|
+
}).catch(() => setConsoleLoadingMore(false));
|
|
1760
|
+
};
|
|
1743
1761
|
const handleSectionChange = (s3) => {
|
|
1744
1762
|
if (s3 === section) {
|
|
1745
1763
|
refreshSection(s3);
|
|
@@ -1760,6 +1778,9 @@
|
|
|
1760
1778
|
setJobStatus("");
|
|
1761
1779
|
setJobDuration("");
|
|
1762
1780
|
setJobSort({ col: null, dir: "asc" });
|
|
1781
|
+
setConsoleSearch("");
|
|
1782
|
+
setConsoleStatus("");
|
|
1783
|
+
setConsoleSort({ col: null, dir: "asc" });
|
|
1763
1784
|
setOutboundSearch("");
|
|
1764
1785
|
setOutboundMethod("");
|
|
1765
1786
|
setOutboundStatus("");
|
|
@@ -1780,6 +1801,11 @@
|
|
|
1780
1801
|
setJobs((prev) => prev.filter((p3) => p3.token !== token));
|
|
1781
1802
|
});
|
|
1782
1803
|
};
|
|
1804
|
+
const deleteConsole = (token) => {
|
|
1805
|
+
fetch(`${BASE}/api/console/${token}`, { method: "DELETE" }).then(() => {
|
|
1806
|
+
setConsoles((prev) => prev.filter((p3) => p3.token !== token));
|
|
1807
|
+
});
|
|
1808
|
+
};
|
|
1783
1809
|
const clearProfiles = () => {
|
|
1784
1810
|
if (!window.confirm("Delete all HTTP profiles?")) return;
|
|
1785
1811
|
fetch(`${BASE}/api/profiles/clear`, { method: "DELETE" }).then(() => {
|
|
@@ -1792,14 +1818,22 @@
|
|
|
1792
1818
|
setJobs([]);
|
|
1793
1819
|
});
|
|
1794
1820
|
};
|
|
1821
|
+
const clearConsole = () => {
|
|
1822
|
+
if (!window.confirm("Delete all console profiles?")) return;
|
|
1823
|
+
fetch(`${BASE}/api/console/clear`, { method: "DELETE" }).then(() => {
|
|
1824
|
+
setConsoles([]);
|
|
1825
|
+
});
|
|
1826
|
+
};
|
|
1795
1827
|
const clearAll = () => {
|
|
1796
|
-
if (!window.confirm("Delete all HTTP and
|
|
1828
|
+
if (!window.confirm("Delete all HTTP, job and console profiles?")) return;
|
|
1797
1829
|
Promise.all([
|
|
1798
1830
|
fetch(`${BASE}/api/profiles/clear`, { method: "DELETE" }),
|
|
1799
|
-
fetch(`${BASE}/api/jobs/clear`, { method: "DELETE" })
|
|
1831
|
+
fetch(`${BASE}/api/jobs/clear`, { method: "DELETE" }),
|
|
1832
|
+
fetch(`${BASE}/api/console/clear`, { method: "DELETE" })
|
|
1800
1833
|
]).then(() => {
|
|
1801
1834
|
setProfiles([]);
|
|
1802
1835
|
setJobs([]);
|
|
1836
|
+
setConsoles([]);
|
|
1803
1837
|
});
|
|
1804
1838
|
};
|
|
1805
1839
|
const refreshSection = (s3) => {
|
|
@@ -1825,6 +1859,17 @@
|
|
|
1825
1859
|
setJobsError("Failed to load job profiles");
|
|
1826
1860
|
setLoadingJobs(false);
|
|
1827
1861
|
});
|
|
1862
|
+
} else if (s3 === "console") {
|
|
1863
|
+
setLoadingConsole(true);
|
|
1864
|
+
fetch(`${BASE}/api/console?limit=50&offset=0`).then((res) => res.json()).then((data) => {
|
|
1865
|
+
setConsoles(data.profiles);
|
|
1866
|
+
setConsoleOffset(data.profiles.length);
|
|
1867
|
+
setConsoleHasMore(data.has_more);
|
|
1868
|
+
setLoadingConsole(false);
|
|
1869
|
+
}).catch(() => {
|
|
1870
|
+
setConsoleError("Failed to load console profiles");
|
|
1871
|
+
setLoadingConsole(false);
|
|
1872
|
+
});
|
|
1828
1873
|
} else if (s3 === "outbound") {
|
|
1829
1874
|
setLoadingOutbound(true);
|
|
1830
1875
|
fetch(`${BASE}/api/outbound_http`).then((res) => res.json()).then((data) => {
|
|
@@ -1846,6 +1891,11 @@
|
|
|
1846
1891
|
}
|
|
1847
1892
|
};
|
|
1848
1893
|
const refresh = () => refreshSection(section);
|
|
1894
|
+
const toggleConsoleSort = (col) => {
|
|
1895
|
+
setConsoleSort(
|
|
1896
|
+
(prev) => prev.col === col ? { col, dir: prev.dir === "asc" ? "desc" : "asc" } : { col, dir: "asc" }
|
|
1897
|
+
);
|
|
1898
|
+
};
|
|
1849
1899
|
const tabClass = (s3) => `tab${section === s3 ? " active" : ""}`;
|
|
1850
1900
|
const filteredProfiles = profiles.filter((p3) => {
|
|
1851
1901
|
if (httpSearch && !p3.path.toLowerCase().includes(httpSearch.toLowerCase())) return false;
|
|
@@ -1933,8 +1983,35 @@
|
|
|
1933
1983
|
}
|
|
1934
1984
|
return true;
|
|
1935
1985
|
});
|
|
1986
|
+
const filteredConsoles = consoles.filter((p3) => {
|
|
1987
|
+
if (consoleSearch && !p3.path.toLowerCase().includes(consoleSearch.toLowerCase())) return false;
|
|
1988
|
+
if (consoleStatus === "error" && p3.status !== 500) return false;
|
|
1989
|
+
if (consoleStatus === "ok" && p3.status === 500) return false;
|
|
1990
|
+
return true;
|
|
1991
|
+
});
|
|
1992
|
+
const sortedConsoles = consoleSort.col ? [...filteredConsoles].sort((a3, b) => {
|
|
1993
|
+
if (consoleSort.col === "date") {
|
|
1994
|
+
const diff = new Date(a3.started_at).getTime() - new Date(b.started_at).getTime();
|
|
1995
|
+
return consoleSort.dir === "asc" ? diff : -diff;
|
|
1996
|
+
}
|
|
1997
|
+
let av, bv;
|
|
1998
|
+
switch (consoleSort.col) {
|
|
1999
|
+
case "duration":
|
|
2000
|
+
av = a3.duration;
|
|
2001
|
+
bv = b.duration;
|
|
2002
|
+
break;
|
|
2003
|
+
case "status":
|
|
2004
|
+
av = a3.status;
|
|
2005
|
+
bv = b.status;
|
|
2006
|
+
break;
|
|
2007
|
+
default:
|
|
2008
|
+
return 0;
|
|
2009
|
+
}
|
|
2010
|
+
return consoleSort.dir === "asc" ? av - bv : bv - av;
|
|
2011
|
+
}) : filteredConsoles;
|
|
1936
2012
|
const httpFiltersActive = !!(httpSearch || httpMethod || httpStatus || httpDuration || httpPreset);
|
|
1937
2013
|
const jobFiltersActive = !!(jobSearch || jobStatus || jobDuration);
|
|
2014
|
+
const consoleFiltersActive = !!(consoleSearch || consoleStatus);
|
|
1938
2015
|
const outboundFiltersActive = !!(outboundSearch || outboundMethod || outboundStatus);
|
|
1939
2016
|
const sortIcon = (activeCol, dir, col) => {
|
|
1940
2017
|
if (activeCol !== col) return /* @__PURE__ */ u3("span", { class: "sort-icon sort-icon--idle", children: "\u21C5" });
|
|
@@ -1962,6 +2039,10 @@
|
|
|
1962
2039
|
e3.preventDefault();
|
|
1963
2040
|
handleSectionChange("jobs");
|
|
1964
2041
|
}, children: "Background Jobs" }),
|
|
2042
|
+
/* @__PURE__ */ u3("a", { href: "#", class: tabClass("console"), onClick: (e3) => {
|
|
2043
|
+
e3.preventDefault();
|
|
2044
|
+
handleSectionChange("console");
|
|
2045
|
+
}, children: "Console" }),
|
|
1965
2046
|
/* @__PURE__ */ u3("div", { style: "flex: 1" }),
|
|
1966
2047
|
/* @__PURE__ */ u3("a", { href: "#", class: tabClass("outbound"), onClick: (e3) => {
|
|
1967
2048
|
e3.preventDefault();
|
|
@@ -2160,6 +2241,83 @@
|
|
|
2160
2241
|
] }),
|
|
2161
2242
|
jobHasMore && !jobFiltersActive && /* @__PURE__ */ u3("div", { class: "profiler-load-more", children: /* @__PURE__ */ u3("button", { class: "btn btn-secondary", onClick: loadMoreJobs, disabled: jobLoadingMore, children: jobLoadingMore ? "Loading\u2026" : "Load more" }) })
|
|
2162
2243
|
] })),
|
|
2244
|
+
section === "console" && (loadingConsole ? /* @__PURE__ */ u3(TableSkeleton, { cols: ["sm", "flex", "sm", "sm", "xs", "sm"] }) : consoleError ? /* @__PURE__ */ u3("div", { class: "profiler-empty", children: /* @__PURE__ */ u3("div", { class: "profiler-empty__title", children: consoleError }) }) : consoles.length === 0 ? /* @__PURE__ */ u3("div", { class: "profiler-empty", children: [
|
|
2245
|
+
/* @__PURE__ */ u3("div", { class: "profiler-empty__title", children: "No console profiles found" }),
|
|
2246
|
+
/* @__PURE__ */ u3("p", { class: "profiler-empty__description", children: [
|
|
2247
|
+
"Run expressions in ",
|
|
2248
|
+
/* @__PURE__ */ u3("code", { children: "rails console" }),
|
|
2249
|
+
" to see profiling data"
|
|
2250
|
+
] })
|
|
2251
|
+
] }) : /* @__PURE__ */ u3(k, { children: [
|
|
2252
|
+
/* @__PURE__ */ u3("div", { class: "profiler-action-bar profiler-mb-3", children: [
|
|
2253
|
+
/* @__PURE__ */ u3("div", { class: "profiler-filter-group", children: [
|
|
2254
|
+
/* @__PURE__ */ u3(
|
|
2255
|
+
"input",
|
|
2256
|
+
{
|
|
2257
|
+
type: "text",
|
|
2258
|
+
class: "profiler-filter-input",
|
|
2259
|
+
placeholder: "Search expression\u2026",
|
|
2260
|
+
value: consoleSearch,
|
|
2261
|
+
onInput: (e3) => setConsoleSearch(e3.target.value)
|
|
2262
|
+
}
|
|
2263
|
+
),
|
|
2264
|
+
/* @__PURE__ */ u3("select", { class: "profiler-filter-select", value: consoleStatus, onChange: (e3) => setConsoleStatus(e3.target.value), children: [
|
|
2265
|
+
/* @__PURE__ */ u3("option", { value: "", children: "All Statuses" }),
|
|
2266
|
+
/* @__PURE__ */ u3("option", { value: "ok", children: "OK" }),
|
|
2267
|
+
/* @__PURE__ */ u3("option", { value: "error", children: "Error" })
|
|
2268
|
+
] })
|
|
2269
|
+
] }),
|
|
2270
|
+
/* @__PURE__ */ u3("div", { class: "profiler-filter-group", children: [
|
|
2271
|
+
consoleFiltersActive && /* @__PURE__ */ u3("span", { class: "profiler-filter-count", children: [
|
|
2272
|
+
filteredConsoles.length,
|
|
2273
|
+
" / ",
|
|
2274
|
+
consoles.length
|
|
2275
|
+
] }),
|
|
2276
|
+
/* @__PURE__ */ u3("button", { class: `btn-refresh${loadingConsole ? " btn-refresh--spinning" : ""}`, onClick: refresh, disabled: loadingConsole, title: "Refresh", children: "\u21BA" }),
|
|
2277
|
+
/* @__PURE__ */ u3("button", { class: "btn btn-danger btn-sm", onClick: clearConsole, title: "Delete console profiles", children: "Clear" }),
|
|
2278
|
+
/* @__PURE__ */ u3("button", { class: "btn btn-danger btn-sm", onClick: clearAll, title: "Delete all profiles", children: "Clear All" })
|
|
2279
|
+
] })
|
|
2280
|
+
] }),
|
|
2281
|
+
filteredConsoles.length === 0 ? /* @__PURE__ */ u3("div", { class: "profiler-empty", children: /* @__PURE__ */ u3("div", { class: "profiler-empty__title", children: "No results match filters" }) }) : /* @__PURE__ */ u3("table", { children: [
|
|
2282
|
+
/* @__PURE__ */ u3("thead", { children: /* @__PURE__ */ u3("tr", { children: [
|
|
2283
|
+
/* @__PURE__ */ u3("th", { class: `sortable${consoleSort.col === "date" ? " sortable--active" : ""}`, onClick: () => toggleConsoleSort("date"), children: [
|
|
2284
|
+
"Time ",
|
|
2285
|
+
sortIcon(consoleSort.col, consoleSort.dir, "date")
|
|
2286
|
+
] }),
|
|
2287
|
+
/* @__PURE__ */ u3("th", { children: "Expression" }),
|
|
2288
|
+
/* @__PURE__ */ u3("th", { class: `sortable${consoleSort.col === "duration" ? " sortable--active" : ""}`, onClick: () => toggleConsoleSort("duration"), children: [
|
|
2289
|
+
"Duration ",
|
|
2290
|
+
sortIcon(consoleSort.col, consoleSort.dir, "duration")
|
|
2291
|
+
] }),
|
|
2292
|
+
/* @__PURE__ */ u3("th", { children: "SQL" }),
|
|
2293
|
+
/* @__PURE__ */ u3("th", { class: `sortable${consoleSort.col === "status" ? " sortable--active" : ""}`, onClick: () => toggleConsoleSort("status"), children: [
|
|
2294
|
+
"Status ",
|
|
2295
|
+
sortIcon(consoleSort.col, consoleSort.dir, "status")
|
|
2296
|
+
] }),
|
|
2297
|
+
/* @__PURE__ */ u3("th", { children: "Token" }),
|
|
2298
|
+
/* @__PURE__ */ u3("th", {})
|
|
2299
|
+
] }) }),
|
|
2300
|
+
/* @__PURE__ */ u3("tbody", { children: sortedConsoles.map((p3) => {
|
|
2301
|
+
const isError = p3.status === 500;
|
|
2302
|
+
return /* @__PURE__ */ u3("tr", { children: [
|
|
2303
|
+
/* @__PURE__ */ u3("td", { children: formatTime(p3.started_at) }),
|
|
2304
|
+
/* @__PURE__ */ u3("td", { children: [
|
|
2305
|
+
/* @__PURE__ */ u3("a", { href: `${BASE}/profiles/${p3.token}`, class: "profiler-text--mono", style: "font-size:0.85em;", children: p3.path.length > 60 ? p3.path.slice(0, 60) + "\u2026" : p3.path }),
|
|
2306
|
+
p3.gem_version && p3.gem_version !== currentVersion && /* @__PURE__ */ u3("span", { class: "profiler-version-warn", title: `Captur\xE9 avec v${p3.gem_version} (actuel : v${currentVersion})`, children: "\u26A0\uFE0F" })
|
|
2307
|
+
] }),
|
|
2308
|
+
/* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: durationClass(p3.duration), children: [
|
|
2309
|
+
p3.duration.toFixed(2),
|
|
2310
|
+
" ms"
|
|
2311
|
+
] }) }),
|
|
2312
|
+
/* @__PURE__ */ u3("td", { children: p3.collectors_data?.database?.total_queries ?? "\u2014" }),
|
|
2313
|
+
/* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: isError ? "badge-error" : "badge-success", children: isError ? "\u2717 Error" : "\u2713 OK" }) }),
|
|
2314
|
+
/* @__PURE__ */ u3("td", { class: "profiler-text--xs profiler-text--mono profiler-text--muted", children: /* @__PURE__ */ u3("button", { class: "token-copy", onClick: () => copyToken(p3.token), title: "Copy full token", children: copiedToken === p3.token ? "\u2713" : p3.token.substring(0, 8) + "\u2026" }) }),
|
|
2315
|
+
/* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("button", { class: "btn-row-delete", onClick: () => deleteConsole(p3.token), title: "Delete", children: "\xD7" }) })
|
|
2316
|
+
] }, p3.token);
|
|
2317
|
+
}) })
|
|
2318
|
+
] }),
|
|
2319
|
+
consoleHasMore && !consoleFiltersActive && /* @__PURE__ */ u3("div", { class: "profiler-load-more", children: /* @__PURE__ */ u3("button", { class: "btn btn-secondary", onClick: loadMoreConsole, disabled: consoleLoadingMore, children: consoleLoadingMore ? "Loading\u2026" : "Load more" }) })
|
|
2320
|
+
] })),
|
|
2163
2321
|
section === "outbound" && (loadingOutbound ? /* @__PURE__ */ u3(TableSkeleton, { cols: ["sm", "xs", "flex", "sm", "xs"], rows: 4 }) : outboundError ? /* @__PURE__ */ u3("div", { class: "profiler-empty", children: /* @__PURE__ */ u3("div", { class: "profiler-empty__title", children: outboundError }) }) : outboundRequests.length === 0 ? /* @__PURE__ */ u3("div", { class: "profiler-empty", children: [
|
|
2164
2322
|
/* @__PURE__ */ u3("div", { class: "profiler-empty__title", children: "No outbound HTTP requests found" }),
|
|
2165
2323
|
/* @__PURE__ */ u3("p", { class: "profiler-empty__description", children: "Make requests to external services to see outbound HTTP data" })
|
|
@@ -4928,6 +5086,106 @@
|
|
|
4928
5086
|
] });
|
|
4929
5087
|
}
|
|
4930
5088
|
|
|
5089
|
+
// app/assets/typescript/profiler/components/dashboard/tabs/ConsoleTab.tsx
|
|
5090
|
+
function ConsoleTab({ data }) {
|
|
5091
|
+
return /* @__PURE__ */ u3("div", { class: "profiler-p-4", children: [
|
|
5092
|
+
/* @__PURE__ */ u3("div", { class: "profiler-mb-6", children: [
|
|
5093
|
+
/* @__PURE__ */ u3("h3", { class: "profiler-section-title profiler-mb-2", children: "Expression" }),
|
|
5094
|
+
/* @__PURE__ */ u3("pre", { class: "profiler-code-block profiler-code-block--full", children: data.expression })
|
|
5095
|
+
] }),
|
|
5096
|
+
data.return_value !== void 0 && /* @__PURE__ */ u3("div", { children: [
|
|
5097
|
+
/* @__PURE__ */ u3("h3", { class: "profiler-section-title profiler-mb-2", children: "Return value" }),
|
|
5098
|
+
/* @__PURE__ */ u3("pre", { class: "profiler-code-block profiler-code-block--full", children: data.return_value })
|
|
5099
|
+
] })
|
|
5100
|
+
] });
|
|
5101
|
+
}
|
|
5102
|
+
|
|
5103
|
+
// app/assets/typescript/profiler/components/dashboard/ConsoleProfileDashboard.tsx
|
|
5104
|
+
function ConsoleProfileDashboard({ profile, initialTab, embedded }) {
|
|
5105
|
+
const cd = profile.collectors_data || {};
|
|
5106
|
+
const hasHttp = cd["http"]?.total_requests > 0;
|
|
5107
|
+
const hasDumps = (cd["dump"]?.count ?? 0) > 0;
|
|
5108
|
+
const hasLogs = (cd["logs"]?.total ?? 0) > 0;
|
|
5109
|
+
const hasException = !!cd["exception"]?.exception_class;
|
|
5110
|
+
const consoleData = cd["console"];
|
|
5111
|
+
const validTabs = ["console", "database", "cache", "http", "dump", "logs", "exception", "env", "timeline"];
|
|
5112
|
+
const defaultTab = validTabs.includes(initialTab) ? initialTab : "console";
|
|
5113
|
+
const [activeTab, setActiveTab] = d2(hasException ? "exception" : defaultTab);
|
|
5114
|
+
const isFailed = profile.status === 500;
|
|
5115
|
+
const handleTabClick = (tab) => (e3) => {
|
|
5116
|
+
e3.preventDefault();
|
|
5117
|
+
setActiveTab(tab);
|
|
5118
|
+
const url = new URL(window.location.href);
|
|
5119
|
+
url.searchParams.set("tab", tab);
|
|
5120
|
+
history.pushState(null, "", url.toString());
|
|
5121
|
+
};
|
|
5122
|
+
const tabClass = (key) => `tab${activeTab === key ? " active" : ""}`;
|
|
5123
|
+
return /* @__PURE__ */ u3("div", { class: "container", children: [
|
|
5124
|
+
/* @__PURE__ */ u3("div", { class: "header", children: [
|
|
5125
|
+
/* @__PURE__ */ u3("h1", { children: /* @__PURE__ */ u3("a", { href: "/_profiler?section=console", children: [
|
|
5126
|
+
/* @__PURE__ */ u3("span", { class: "h1-emoji", children: ">_" }),
|
|
5127
|
+
" Console Profile"
|
|
5128
|
+
] }) }),
|
|
5129
|
+
/* @__PURE__ */ u3("p", { style: "font-family: monospace; word-break: break-all", children: profile.path }),
|
|
5130
|
+
/* @__PURE__ */ u3("div", { class: "profiler-flex profiler-flex--gap-4 profiler-mt-2", children: [
|
|
5131
|
+
/* @__PURE__ */ u3("span", { children: [
|
|
5132
|
+
"Duration: ",
|
|
5133
|
+
/* @__PURE__ */ u3("strong", { children: [
|
|
5134
|
+
profile.duration.toFixed(2),
|
|
5135
|
+
" ms"
|
|
5136
|
+
] })
|
|
5137
|
+
] }),
|
|
5138
|
+
/* @__PURE__ */ u3("span", { children: [
|
|
5139
|
+
"Status: ",
|
|
5140
|
+
/* @__PURE__ */ u3("strong", { children: /* @__PURE__ */ u3("span", { class: `badge-${isFailed ? "error" : "success"}`, children: isFailed ? "Error" : "OK" }) })
|
|
5141
|
+
] }),
|
|
5142
|
+
profile.memory != null && /* @__PURE__ */ u3("span", { children: [
|
|
5143
|
+
"Memory: ",
|
|
5144
|
+
/* @__PURE__ */ u3("strong", { children: [
|
|
5145
|
+
(profile.memory / 1024 / 1024).toFixed(2),
|
|
5146
|
+
" MB"
|
|
5147
|
+
] })
|
|
5148
|
+
] }),
|
|
5149
|
+
profile.gem_version && /* @__PURE__ */ u3("span", { class: "profiler-version-badge", children: [
|
|
5150
|
+
"v",
|
|
5151
|
+
profile.gem_version
|
|
5152
|
+
] })
|
|
5153
|
+
] }),
|
|
5154
|
+
profile.gem_version && profile.gem_version !== getGemVersion() && /* @__PURE__ */ u3("div", { class: "profiler-version-mismatch", children: [
|
|
5155
|
+
"\u26A0\uFE0F Profil captur\xE9 avec la version ",
|
|
5156
|
+
/* @__PURE__ */ u3("strong", { children: profile.gem_version }),
|
|
5157
|
+
" \u2014 version actuelle : ",
|
|
5158
|
+
/* @__PURE__ */ u3("strong", { children: getGemVersion() })
|
|
5159
|
+
] })
|
|
5160
|
+
] }),
|
|
5161
|
+
/* @__PURE__ */ u3("div", { class: "profiler-panel", children: [
|
|
5162
|
+
/* @__PURE__ */ u3("div", { class: "tabs", children: [
|
|
5163
|
+
/* @__PURE__ */ u3("a", { href: "#", class: tabClass("console"), onClick: handleTabClick("console"), children: "Console" }),
|
|
5164
|
+
/* @__PURE__ */ u3("a", { href: "#", class: tabClass("database"), onClick: handleTabClick("database"), children: "Database" }),
|
|
5165
|
+
/* @__PURE__ */ u3("a", { href: "#", class: tabClass("cache"), onClick: handleTabClick("cache"), children: "Cache" }),
|
|
5166
|
+
hasHttp && /* @__PURE__ */ u3("a", { href: "#", class: tabClass("http"), onClick: handleTabClick("http"), children: "HTTP" }),
|
|
5167
|
+
hasDumps && /* @__PURE__ */ u3("a", { href: "#", class: tabClass("dump"), onClick: handleTabClick("dump"), children: "Dumps" }),
|
|
5168
|
+
hasLogs && /* @__PURE__ */ u3("a", { href: "#", class: tabClass("logs"), onClick: handleTabClick("logs"), children: "Logs" }),
|
|
5169
|
+
hasException && /* @__PURE__ */ u3("a", { href: "#", class: tabClass("exception"), onClick: handleTabClick("exception"), children: "Exception" }),
|
|
5170
|
+
/* @__PURE__ */ u3("a", { href: "#", class: tabClass("env"), onClick: handleTabClick("env"), children: "Env" }),
|
|
5171
|
+
/* @__PURE__ */ u3("a", { href: "#", class: tabClass("timeline"), onClick: handleTabClick("timeline"), children: "Timeline" })
|
|
5172
|
+
] }),
|
|
5173
|
+
/* @__PURE__ */ u3("div", { class: "profiler-p-0 tab-content active", children: [
|
|
5174
|
+
activeTab === "console" && consoleData && /* @__PURE__ */ u3(ConsoleTab, { data: consoleData }),
|
|
5175
|
+
activeTab === "database" && /* @__PURE__ */ u3(DatabaseTab, { dbData: cd["database"], token: profile.token }),
|
|
5176
|
+
activeTab === "cache" && /* @__PURE__ */ u3(CacheTab, { cacheData: cd["cache"] }),
|
|
5177
|
+
activeTab === "http" && hasHttp && /* @__PURE__ */ u3(HttpTab, { httpData: cd["http"] }),
|
|
5178
|
+
activeTab === "dump" && hasDumps && /* @__PURE__ */ u3(DumpsTab, { dumpData: cd["dump"] }),
|
|
5179
|
+
activeTab === "logs" && hasLogs && /* @__PURE__ */ u3(LogsTab, { logData: cd["logs"] }),
|
|
5180
|
+
activeTab === "exception" && hasException && /* @__PURE__ */ u3(ExceptionTab, { exceptionData: cd["exception"] }),
|
|
5181
|
+
activeTab === "env" && /* @__PURE__ */ u3(EnvTab, { envData: cd["env"], readOnly: true }),
|
|
5182
|
+
activeTab === "timeline" && /* @__PURE__ */ u3(FlameGraphTab, { flamegraphData: cd["flamegraph"], perfData: cd["performance"], functionProfileData: cd["function_profile"] })
|
|
5183
|
+
] })
|
|
5184
|
+
] }),
|
|
5185
|
+
!embedded && /* @__PURE__ */ u3("div", { class: "profiler-mt-6", children: /* @__PURE__ */ u3("a", { href: "/_profiler", style: "color: var(--profiler-accent);", children: "\u2190 Back to profiles" }) })
|
|
5186
|
+
] });
|
|
5187
|
+
}
|
|
5188
|
+
|
|
4931
5189
|
// app/assets/typescript/profiler/timeline.ts
|
|
4932
5190
|
function initTimeline(container) {
|
|
4933
5191
|
console.log("Initializing profiler timeline");
|
|
@@ -5189,6 +5447,8 @@ Duration: ${event.duration.toFixed(2)}ms`;
|
|
|
5189
5447
|
const embedded = showEl.dataset.embedded === "true";
|
|
5190
5448
|
if (profile.profile_type === "job") {
|
|
5191
5449
|
J(/* @__PURE__ */ u3(JobProfileDashboard, { profile, initialTab: tab, embedded }), showEl);
|
|
5450
|
+
} else if (profile.profile_type === "console") {
|
|
5451
|
+
J(/* @__PURE__ */ u3(ConsoleProfileDashboard, { profile, initialTab: tab, embedded }), showEl);
|
|
5192
5452
|
} else {
|
|
5193
5453
|
J(/* @__PURE__ */ u3(ProfileDashboard, { profile, initialTab: tab, embedded }), showEl);
|
|
5194
5454
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Profiler
|
|
4
|
+
module Api
|
|
5
|
+
class ConsoleController < ApplicationController
|
|
6
|
+
skip_before_action :verify_authenticity_token
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
limit = (params[:limit] || 50).to_i
|
|
10
|
+
offset = (params[:offset] || 0).to_i
|
|
11
|
+
all = Profiler.storage.list(limit: 1000, offset: 0)
|
|
12
|
+
console = all.select { |p| p.profile_type == "console" }
|
|
13
|
+
page = console.drop(offset).first(limit + 1)
|
|
14
|
+
render json: {
|
|
15
|
+
profiles: page.first(limit).map(&:to_h),
|
|
16
|
+
limit: limit,
|
|
17
|
+
offset: offset,
|
|
18
|
+
has_more: page.size > limit
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def show
|
|
23
|
+
profile = Profiler.storage.load(params[:id])
|
|
24
|
+
|
|
25
|
+
unless profile && profile.profile_type == "console"
|
|
26
|
+
return render json: { error: "Console profile not found" }, status: :not_found
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
render json: profile.to_h
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def destroy
|
|
33
|
+
profile = Profiler.storage.load(params[:id])
|
|
34
|
+
return render json: { error: "Console profile not found" }, status: :not_found unless profile&.profile_type == "console"
|
|
35
|
+
|
|
36
|
+
Profiler.storage.delete(params[:id])
|
|
37
|
+
head :no_content
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def clear
|
|
41
|
+
Profiler.storage.clear(type: "console")
|
|
42
|
+
head :no_content
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -9,7 +9,7 @@ module Profiler
|
|
|
9
9
|
limit = (params[:limit] || 50).to_i
|
|
10
10
|
offset = (params[:offset] || 0).to_i
|
|
11
11
|
all = Profiler.storage.list(limit: 1000, offset: 0)
|
|
12
|
-
http = all.
|
|
12
|
+
http = all.select { |p| p.profile_type == "http" }
|
|
13
13
|
page = http.drop(offset).first(limit + 1)
|
|
14
14
|
render json: {
|
|
15
15
|
profiles: page.first(limit).map(&:to_h),
|
data/config/routes.rb
CHANGED
|
@@ -29,6 +29,9 @@ Profiler::Engine.routes.draw do
|
|
|
29
29
|
resources :jobs, only: [:index, :show, :destroy] do
|
|
30
30
|
collection { delete :clear }
|
|
31
31
|
end
|
|
32
|
+
resources :console, only: [:index, :show, :destroy] do
|
|
33
|
+
collection { delete :clear }
|
|
34
|
+
end
|
|
32
35
|
resources :outbound_http, only: [:index]
|
|
33
36
|
get "toolbar/:token", to: "toolbar#show"
|
|
34
37
|
post "ajax/link", to: "ajax#link"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base_collector"
|
|
4
|
+
|
|
5
|
+
module Profiler
|
|
6
|
+
module Collectors
|
|
7
|
+
class ConsoleCollector < BaseCollector
|
|
8
|
+
def initialize(profile, expression:)
|
|
9
|
+
super(profile)
|
|
10
|
+
@expression = expression
|
|
11
|
+
@return_value = nil
|
|
12
|
+
@return_value_captured = false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def set_return_value(value)
|
|
16
|
+
@return_value = value.inspect.slice(0, 10_000)
|
|
17
|
+
@return_value_captured = true
|
|
18
|
+
rescue
|
|
19
|
+
@return_value = "(uninspectable)"
|
|
20
|
+
@return_value_captured = true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def icon
|
|
24
|
+
">_"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def priority
|
|
28
|
+
5
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def tab_config
|
|
32
|
+
{
|
|
33
|
+
key: "console",
|
|
34
|
+
label: "Console",
|
|
35
|
+
icon: icon,
|
|
36
|
+
priority: priority,
|
|
37
|
+
enabled: true,
|
|
38
|
+
default_active: true
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def collect
|
|
43
|
+
data = { expression: @expression }
|
|
44
|
+
data[:return_value] = @return_value if @return_value_captured
|
|
45
|
+
store_data(data)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def has_data?
|
|
49
|
+
@expression.to_s.length > 0
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def toolbar_summary
|
|
53
|
+
{ text: @expression.to_s[0, 30], color: "blue" }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -11,6 +11,7 @@ module Profiler
|
|
|
11
11
|
:track_ajax, :ajax_skip_paths,
|
|
12
12
|
:track_http, :slow_http_threshold, :http_skip_hosts,
|
|
13
13
|
:track_jobs,
|
|
14
|
+
:track_console,
|
|
14
15
|
:track_mailers, :capture_mail_body, :sanitize_mailer_recipients, :mailer_skip_actions,
|
|
15
16
|
:compress_bodies, :compress_body_threshold
|
|
16
17
|
|
|
@@ -42,6 +43,7 @@ module Profiler
|
|
|
42
43
|
@slow_http_threshold = 500 # milliseconds
|
|
43
44
|
@http_skip_hosts = []
|
|
44
45
|
@track_jobs = true
|
|
46
|
+
@track_console = true
|
|
45
47
|
@track_mailers = true
|
|
46
48
|
@capture_mail_body = false
|
|
47
49
|
@sanitize_mailer_recipients = false
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "models/profile"
|
|
4
|
+
require_relative "current_context"
|
|
5
|
+
require_relative "collectors/console_collector"
|
|
6
|
+
require_relative "collectors/database_collector"
|
|
7
|
+
require_relative "collectors/cache_collector"
|
|
8
|
+
require_relative "collectors/http_collector"
|
|
9
|
+
require_relative "collectors/dump_collector"
|
|
10
|
+
require_relative "collectors/log_collector"
|
|
11
|
+
require_relative "collectors/exception_collector"
|
|
12
|
+
require_relative "collectors/env_collector"
|
|
13
|
+
require_relative "collectors/flamegraph_collector"
|
|
14
|
+
|
|
15
|
+
module Profiler
|
|
16
|
+
class ConsoleProfiler
|
|
17
|
+
CONSOLE_COLLECTOR_CLASSES = [
|
|
18
|
+
Collectors::DatabaseCollector,
|
|
19
|
+
Collectors::CacheCollector,
|
|
20
|
+
Collectors::HttpCollector,
|
|
21
|
+
Collectors::DumpCollector,
|
|
22
|
+
Collectors::LogCollector,
|
|
23
|
+
Collectors::ExceptionCollector,
|
|
24
|
+
Collectors::EnvCollector,
|
|
25
|
+
Collectors::FlameGraphCollector
|
|
26
|
+
].freeze
|
|
27
|
+
|
|
28
|
+
def self.profile(expression:, &block)
|
|
29
|
+
return block.call unless Profiler.enabled? && Profiler.configuration.track_console
|
|
30
|
+
|
|
31
|
+
new(expression: expression).run(&block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def initialize(expression:)
|
|
35
|
+
@expression = expression
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def run(&block)
|
|
39
|
+
profile = Models::Profile.new
|
|
40
|
+
profile.profile_type = "console"
|
|
41
|
+
profile.gem_version = Profiler::VERSION
|
|
42
|
+
profile.path = @expression.length > 200 ? "#{@expression[0, 200]}..." : @expression
|
|
43
|
+
profile.method = "CONSOLE"
|
|
44
|
+
|
|
45
|
+
console_collector = Collectors::ConsoleCollector.new(profile, expression: @expression)
|
|
46
|
+
collectors = [console_collector] + CONSOLE_COLLECTOR_CLASSES.map { |klass| klass.new(profile) }
|
|
47
|
+
collectors.each { |c| c.subscribe if c.respond_to?(:subscribe) }
|
|
48
|
+
|
|
49
|
+
exception_collector = collectors.find { |c| c.is_a?(Collectors::ExceptionCollector) }
|
|
50
|
+
|
|
51
|
+
memory_before = current_memory if Profiler.configuration.track_memory
|
|
52
|
+
|
|
53
|
+
console_status = "completed"
|
|
54
|
+
|
|
55
|
+
previous_token = Profiler::CurrentContext.token
|
|
56
|
+
Profiler::CurrentContext.token = profile.token
|
|
57
|
+
result = nil
|
|
58
|
+
begin
|
|
59
|
+
result = block.call
|
|
60
|
+
console_collector.set_return_value(result)
|
|
61
|
+
result
|
|
62
|
+
rescue => e
|
|
63
|
+
console_status = "failed"
|
|
64
|
+
exception_collector&.capture(e)
|
|
65
|
+
raise
|
|
66
|
+
ensure
|
|
67
|
+
Profiler::CurrentContext.token = previous_token
|
|
68
|
+
if Profiler.configuration.track_memory
|
|
69
|
+
profile.memory = current_memory - memory_before
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
profile.finish(console_status == "completed" ? 200 : 500)
|
|
73
|
+
|
|
74
|
+
collectors.each do |collector|
|
|
75
|
+
begin
|
|
76
|
+
collector.collect if collector.respond_to?(:collect)
|
|
77
|
+
profile.add_collector_metadata(collector)
|
|
78
|
+
rescue => e
|
|
79
|
+
warn "Profiler ConsoleProfiler: Collector #{collector.class} failed: #{e.message}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
Profiler.storage.save(profile.token, profile)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def current_memory
|
|
90
|
+
return 0 unless defined?(GC.stat)
|
|
91
|
+
|
|
92
|
+
stats = GC.stat
|
|
93
|
+
if stats.key?(:total_allocated_size)
|
|
94
|
+
stats[:total_allocated_size]
|
|
95
|
+
elsif stats.key?(:total_allocated_objects)
|
|
96
|
+
stats[:total_allocated_objects] * 40
|
|
97
|
+
else
|
|
98
|
+
0
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Profiler
|
|
4
|
+
module Instrumentation
|
|
5
|
+
module IrbInstrumentation
|
|
6
|
+
IRB_SKIP_COMMANDS = %w[exit quit exit! irb help].freeze
|
|
7
|
+
|
|
8
|
+
def evaluate(line, line_no, *args)
|
|
9
|
+
Profiler.env_override_store.apply!
|
|
10
|
+
stripped = line.to_s.strip
|
|
11
|
+
skip = !Profiler.enabled? ||
|
|
12
|
+
!Profiler.configuration.track_console ||
|
|
13
|
+
stripped.empty? ||
|
|
14
|
+
IRB_SKIP_COMMANDS.include?(stripped)
|
|
15
|
+
return super if skip
|
|
16
|
+
|
|
17
|
+
Profiler::ConsoleProfiler.profile(expression: stripped) { super }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/profiler/railtie.rb
CHANGED
|
@@ -81,6 +81,14 @@ module Profiler
|
|
|
81
81
|
end
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
console do
|
|
85
|
+
next unless Profiler.configuration.enabled && Profiler.configuration.track_console
|
|
86
|
+
|
|
87
|
+
require_relative "console_profiler"
|
|
88
|
+
require_relative "instrumentation/irb_instrumentation"
|
|
89
|
+
IRB::Context.prepend(Profiler::Instrumentation::IrbInstrumentation)
|
|
90
|
+
end
|
|
91
|
+
|
|
84
92
|
rake_tasks do
|
|
85
93
|
load "profiler/tasks/profiler.rake"
|
|
86
94
|
end
|
data/lib/profiler/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-profiler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.25.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sébastien Duplessy
|
|
@@ -107,6 +107,7 @@ files:
|
|
|
107
107
|
- app/assets/builds/profiler.css
|
|
108
108
|
- app/assets/builds/profiler.js
|
|
109
109
|
- app/controllers/profiler/api/ajax_controller.rb
|
|
110
|
+
- app/controllers/profiler/api/console_controller.rb
|
|
110
111
|
- app/controllers/profiler/api/env_vars_controller.rb
|
|
111
112
|
- app/controllers/profiler/api/explain_controller.rb
|
|
112
113
|
- app/controllers/profiler/api/function_profiling_controller.rb
|
|
@@ -127,6 +128,7 @@ files:
|
|
|
127
128
|
- lib/profiler/collectors/ajax_collector.rb
|
|
128
129
|
- lib/profiler/collectors/base_collector.rb
|
|
129
130
|
- lib/profiler/collectors/cache_collector.rb
|
|
131
|
+
- lib/profiler/collectors/console_collector.rb
|
|
130
132
|
- lib/profiler/collectors/database_collector.rb
|
|
131
133
|
- lib/profiler/collectors/dump_collector.rb
|
|
132
134
|
- lib/profiler/collectors/env_collector.rb
|
|
@@ -142,11 +144,13 @@ files:
|
|
|
142
144
|
- lib/profiler/collectors/routes_collector.rb
|
|
143
145
|
- lib/profiler/collectors/view_collector.rb
|
|
144
146
|
- lib/profiler/configuration.rb
|
|
147
|
+
- lib/profiler/console_profiler.rb
|
|
145
148
|
- lib/profiler/current_context.rb
|
|
146
149
|
- lib/profiler/engine.rb
|
|
147
150
|
- lib/profiler/env_override_store.rb
|
|
148
151
|
- lib/profiler/explain_runner.rb
|
|
149
152
|
- lib/profiler/instrumentation/active_job_instrumentation.rb
|
|
153
|
+
- lib/profiler/instrumentation/irb_instrumentation.rb
|
|
150
154
|
- lib/profiler/instrumentation/net_http_instrumentation.rb
|
|
151
155
|
- lib/profiler/instrumentation/sidekiq_middleware.rb
|
|
152
156
|
- lib/profiler/instrumentation/thread_context_propagation.rb
|