Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@
},
"dependencies": {
"@datadog/pprof": "^5.4.1",
"axios": ">=0.29.0 <1.x",
"debug": "^4.3.3",
"form-data": "^4.0.0",
"p-limit": "^3.1.0",
"regenerator-runtime": "^0.13.11",
"source-map": "^0.7.3"
Expand Down
73 changes: 30 additions & 43 deletions src/pyroscope-api-exporter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { URL } from 'node:url';

import { encode } from '@datadog/pprof';
import axios, { AxiosBasicCredentials, AxiosError } from 'axios';
import FormData, { Headers } from 'form-data';
import { Profile } from 'pprof-format';
import { ProfileExport, ProfileExporter } from './profile-exporter.js';
import { dateToUnixTimestamp } from './utils/date-to-unix-timestamp.js';
Expand Down Expand Up @@ -58,15 +56,22 @@ export class PyroscopeApiExporter implements ProfileExporter {
return endpointUrl;
}

private buildRequestHeaders(formData: FormData): Headers {
const headers: Headers = formData.getHeaders();
private buildRequestHeaders(): Headers {
const headers: Headers = new Headers();

if (this.authToken !== undefined) {
headers['authorization'] = `Bearer ${this.authToken}`;
headers.set('authorization', `Bearer ${this.authToken}`);
} else if (this.config.basicAuthUser && this.config.basicAuthPassword) {
headers.set(
'authorization',
Buffer.from(
`${this.config.basicAuthUser}:${this.config.basicAuthPassword}`
).toString('base64')
);
}

if (this.config.tenantID) {
headers['X-Scope-OrgID'] = this.config.tenantID;
headers.set('X-Scope-OrgID', this.config.tenantID);
}

return headers;
Expand All @@ -81,54 +86,36 @@ export class PyroscopeApiExporter implements ProfileExporter {

const formData: FormData = new FormData();

formData.append('profile', profileBuffer, {
contentType: 'text/json',
filename: 'profile',
knownLength: profileBuffer.byteLength,
});
formData.append('profile', new Blob([profileBuffer]), 'profile');

return formData;
}

private handleAxiosError(error: AxiosError): void {
if (error.response !== undefined) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
log('Pyroscope received error while ingesting data to server');
log(error.response.data);
} else if (error.request !== undefined) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
log('Error when ingesting data to server:', error.message);
} else {
// Something happened in setting up the request that triggered an Error
log('Error', error.message);
}
}

private async uploadProfile(profileExport: ProfileExport): Promise<void> {
const formData: FormData = await this.buildUploadProfileFormData(
profileExport.profile
);

const auth: AxiosBasicCredentials | undefined =
this.config.basicAuthUser && this.config.basicAuthPassword
? {
username: this.config.basicAuthUser,
password: this.config.basicAuthPassword,
}
: undefined;

try {
await axios(this.buildEndpointUrl(profileExport).toString(), {
data: formData,
headers: this.buildRequestHeaders(formData),
method: 'POST',
auth: auth,
});
const response = await fetch(
this.buildEndpointUrl(profileExport).toString(),
{
body: formData,
headers: this.buildRequestHeaders(),
method: 'POST',
}
);

if (!response.ok) {
log('Server rejected data ingest: HTTP %d', response.status);
log(await response.text());
}
} catch (error: unknown) {
this.handleAxiosError(error as AxiosError);
if (error instanceof Error) {
log('Error sending data ingest: %s', error.message);
} else {
log('Unknown error sending data ingest');
}
}
}
}
19 changes: 0 additions & 19 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -748,15 +748,6 @@ asynckit@^0.4.0:
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==

"axios@>=0.29.0 <1.x":
version "0.29.0"
resolved "https://registry.npmjs.org/axios/-/axios-0.29.0.tgz#5eed1a0bc4c0ffe060624eb7900aff66b7881eeb"
integrity sha512-Kjsq1xisgO5DjjNQwZFsy0gpcU1P2j36dZeQDXVhpIU26GVgkDUnROaHLSuluhMqtDE7aKA2hbKXG5yu5DN8Tg==
dependencies:
follow-redirects "^1.15.4"
form-data "^4.0.0"
proxy-from-env "^1.1.0"

balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
Expand Down Expand Up @@ -1319,11 +1310,6 @@ flatted@^3.2.9:
resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz"
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==

follow-redirects@^1.15.4:
version "1.15.9"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==

foreground-child@^3.1.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77"
Expand Down Expand Up @@ -2030,11 +2016,6 @@ proxy-addr@~2.0.7:
forwarded "0.2.0"
ipaddr.js "1.9.1"

proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==

punycode@^2.1.0:
version "2.3.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz"
Expand Down