Capture request body using Opentelemetry in node.js - javascript

I am trying to capture response body using otel-instrumentation-http and responseHook. But it doesn't capture the response body sent by the server.
tracing.js
// OTel
const { Resource } = require('#opentelemetry/resources');
const {
SemanticResourceAttributes,
} = require('#opentelemetry/semantic-conventions');
const {http_1} = require('http');
// SDKs
const { NodeTracerProvider, ConsoleSpanExporter, SimpleSpanProcessor } = require('#opentelemetry/sdk-trace-node');
// Instrumentation
const { HttpInstrumentation } = require('#opentelemetry/instrumentation-http');
const { registerInstrumentations } = require('#opentelemetry/instrumentation');
const { ExpressInstrumentation } = require('#opentelemetry/instrumentation-express');
// Exporters
const { OTLPTraceExporter } = require('#opentelemetry/exporter-trace-otlp-http');
const exporter = new OTLPTraceExporter({
url: 'http://localhost:4318/v1/traces'
});
const initialize = function(config) {
const provider = new NodeTracerProvider({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]:
config.serviceName,
}),
});
// export spans to console (useful for debugging)
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
// export spans to opentelemetry collector
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
provider.register();
registerInstrumentations({
instrumentations: [
new HttpInstrumentation({
responseHook: (span, response) => {
response.on('data', (chunk) => {
if(!response['myBody']){
response['myBody'] = [];
response['myBody'].push(chunk);
}
else{
response['myBody'].push(chunk);
}
});
response.on('end', () => {
const responseBody = Buffer.concat(response['myBody']).toString();
console.log('respnse body is', responseBody);
console.log('response headers are', response.headers);
span.setAttribute('http.response.body',responseBody);
span.setAttributes('http.response.headers', response.headers);
});
}
}),
new ExpressInstrumentation({
ignoreLayersType: ['router', 'middleware'],
requestHook: (span, requestInfo) => {
span.setAttribute(
'http.request.request_body',
JSON.stringify(requestInfo.request.body)
);
span.setAttribute(
'http.request.query_params',
JSON.stringify(requestInfo.request.query)
);
}
})
]
});
};
module.exports = {initialize};
app.js
const { initialize } = require('./tracing');
initialize({serviceName: 'api-observability-orchestrator'});
const express = require('express');
const port = process.env.PORT || '8080';
const app = express();
app.use(express.json());
app.get('/', (req, resp) => {
resp.send({ message: 'Hello World !!' });
});
app.get('/path/:name', (req, resp) => {
resp.send({ message: `Hello ${req.params.name} !!` });
});
app.get('/query', (req, resp) => {
resp.send({ message: `Hello ${req.query.name} !!` });
});
app.post('/payload', (req, res) => {
res.json({ requestBody: req.body });
});
app.listen(parseInt(port, 10), () => {
console.log('Listening for requests on localhost:', port);
});
I have looked at the docs, and similary github issue: github-3104tried instrumenting by adding listeners inside the library function _traceClientRequest itself.
Output:
respnse body is {"partialSuccess":{}}
response headers are {
'content-type': 'application/json',
date: 'Fri, 27 Jan 2023 16:02:17 GMT',
'content-length': '21'
}
Expectation
<actual response body>

Related

Using Axios to write to MongoDB

I'm using nuxtjs/axios and Mongoose to write to MongoDB. The POST always works but it takes a few seconds for the insert to get into MongoDB. Problem is that I'm trying to call a GET immediately after a new POST so i can get all the latest records. That doesn't always happen because it takes a few seconds for the data to get into the DB. Here's my index.js file for the server:
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
const Post = require('./models/post');
const express = require('express');
const { Nuxt, Builder } = require('nuxt');
const app = express();
const mongoose = require('mongoose');
const xss = require('xss-clean');
app.use(
express.urlencoded({
extended: true
})
)
app.use(express.json())
app.use(xss());
const config = require('../nuxt.config.js');
config.dev = process.env.NODE_ENV !== 'production';
const nuxt = new Nuxt(config);
const { host, port } = nuxt.options.server;
const username = process.env.username;
const pwd = process.env.pwd;
const server = process.env.server;
const db = process.env.db;
const dbURI = `mongodb+srv://${username}:${pwd}#${server}/${db}?
retryWrites=true&w=majority`;
async function start() {
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
} else {
await nuxt.ready();
}
app.use(nuxt.render);
}
start();
mongoose
.connect(dbURI, {useNewUrlParser: true, useUnifiedTopology: true})
.then((result) => {
app.listen(port, host); // listen
}
)
.catch(err => console.log(err));
app.get('/posts', (req, res) => {
Post
.find()
.sort({createdAt: -1})
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
})
app.post(
'/posts',
(req, res) => {
const post = new Post({
body: req.body.post.trim()
});
post
.save()
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
}
);
I feel like in app.post the .save() isn't waiting for the insert to complete. Is my implementation wrong? Here's my Store:
export const actions = {
async getPosts() {
let res = await this.$axios.get(`/posts`);
return res;
}
}
export const mutations = {
async savePost(state, data) {
let res = await this.$axios.post('/posts', {post: data});
return res;
}
}
And here's my index.vue file:
export default {
components: {},
data: () => ({
posts:[],
confession: ""
}),
mounted(){
this.getPosts();
},
methods: {
async getPosts() {
let res = await this.$store.dispatch('getPosts');
this.posts = res;
},
async savePost(payload) {
let res = await this.$store.commit('savePost', payload);
return res;
},
clear(){
this.confession = "";
},
focusInput() {
this.$refs.confession.focus();
},
onSubmit() {
this
.savePost(this.confession.trim())
.then((result) => {
this.playSound();
this.getPosts();
this.clear();
this.focusInput();
});
},
playSound: function(){
// sound code
}
}
}
}
Maybe you can try to add w: "majority" option in save method.
Mongoose Documentation of save options
MongoDB Documentation for further explanation of 'writeConcern'
app.post(
'/posts',
(req, res) => {
const post = new Post({
body: req.body.post.trim()
});
post
.save({w: "majority"})
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
}
);

How to use authenticated proxies with undici

Hello so am trying to use undici with a proxy but it doesn't work i tired this
const client = new Client({
url: 'www.google.com',
proxy: 'http://user:pass#host:port'
})
as well as this
const { HttpsProxyAgent } = require("https-proxy-agent");
const proxy = new HttpsProxyAgent("http://user:pass#host:port");
time = new Date()
client.request({
path: '/',
method: 'GET',
httpsAgent: proxy
},
but nothing seems to work
See this link here:
https://github.com/nodejs/undici/blob/01302e6d2b2629cca4ad9327abe0f7a317f8399f/docs/best-practices/proxy.md#connect-with-authentication
import { Client } from 'undici'
import { createServer } from 'http'
import proxy from 'proxy'
const server = await buildServer()
const proxy = await buildProxy()
const serverUrl = `http://localhost:${server.address().port}`
const proxyUrl = `http://localhost:${proxy.address().port}`
proxy.authenticate = function (req, fn) {
fn(null, req.headers['proxy-authorization'] === `Basic ${Buffer.from('user:pass').toString('base64')}`)
}
server.on('request', (req, res) => {
console.log(req.url) // '/hello?foo=bar'
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ hello: 'world' }))
})
const client = new Client(proxyUrl)
const response = await client.request({
method: 'GET',
path: serverUrl + '/hello?foo=bar',
headers: {
'proxy-authorization': `Basic ${Buffer.from('user:pass').toString('base64')}`
}
})
response.body.setEncoding('utf8')
let data = ''
for await (const chunk of response.body) {
data += chunk
}
console.log(response.statusCode) // 200
console.log(JSON.parse(data)) // { hello: 'world' }
server.close()
proxy.close()
client.close()
function buildServer () {
return new Promise((resolve, reject) => {
const server = createServer()
server.listen(0, () => resolve(server))
})
}
function buildProxy () {
return new Promise((resolve, reject) => {
const server = proxy(createServer())
server.listen(0, () => resolve(server))
})
}

i am trying to deploy my firebase function below written js?

const functions = require('firebase-functions');
const cors = require('cors')({ origin: true });
const Busboy = require('busboy');
const os = require('os');
const path = require('path');
const fs = require('fs');
const fbAdmin = require('firebase-admin');
const uuid = require('uuid/v4');
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
const gcconfig = {
' '
};
const gcs = require('#google-cloud/storage')(gcconfig);
fbAdmin.initializeApp({ credential: fbAdmin.credential.cert(require('')) });
exports.storeImage = functions.https.onRequest((req, res) => {
return cors(req, res, () => {
if (req.method !== 'POST') {
return res.status(500).json({ message: 'Not allowed.' });
}
if (!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized.' });
}
let idToken;
idToken = req.headers.authorization.split('Bearer ')[1];
const busboy = new Busboy({ headers: req.headers });
let uploadData;
let oldImagePath;
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const filePath = path.join(os.tmpdir(), filename);
uploadData = { filePath: filePath, type: mimetype, name: filename };
file.pipe(fs.createWriteStream(filePath));
});
busboy.on('field', (fieldname, value) => {
oldImagePath = decodeURIComponent(path);
});
busboy.on('finish', () => {
const bucket = gcs.bucket(' ');
const id = uuid();
let imagePath = 'images/' + id + '-' + uploadData.name
if (oldImagePath) {
imagePath = oldImagePath;
}
return fbAdmin.auth().verifyIdToken(idToken).then(decodedToken => {
return bucket.upload(uploadData.filePath, {
uploadType: 'media',
destination: imagePath,
metadata: {
metadata: {
contentType: uploadData.type,
firebaseStorageDownloadToken: id
}
}
});
}).then(() => {
return res.status(201).json({
imageUrl: 'https://firebasestorage.googleapis.com/v0/b/' + bucket.name + '/o/' + encodeURIComponent(imagePath) + '?alt=media&token=' + id,
imagePath: imagePath
});
}).catch(error => {
return res.status(401).json({ error: 'Unauthorized!' });
});
});
return busboy.end(req.rawBody);
});
});
iam trying to deploy this function but i got this error(Error occurred while parsing your function triggers.
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './v4' is not defined by "exports" in C:\Users\ahmed aziz\AndroidStudioProjects\salers_demo\functions\node_modules\uuid\package.json). so please help me
The error is complaining about your usage of require('uuid/v4').
The API documentation for uuid suggests that you import and use it like this:
import { v4 as uuidv4 } from 'uuid';
uuidv4(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'

Firebase function storage POST image issue

I have encountered a problem when following a maximilian schwarzmüller course, which has otherwise been great: https://www.youtube.com/watch?v=qZ1EFnFOGvE
The image logs in the Firebase console as uploaded, recognises the type of file/size etc. But continually loads and never displays the image. I use a post request in POSTMAN to upload the image.
When I upload manually to firebase on their UI, everything works fine.
My code:
const functions = require("firebase-functions");
const os = require("os");
const path = require("path");
const spawn = require("child-process-promise").spawn;
const cors = require("cors")({ origin: true });
const Busboy = require("busboy");
const fs = require("fs");
const gcconfig = {
projectId: "REDACTED",
keyFilename: "REDACTED"
};
const gcs = require("#google-cloud/storage")(gcconfig);
//
exports.onFileChange = functions.storage.object().onFinalize(event => {
const object = event.data;
const bucket = object.bucket;
const contentType = object.contentType;
const filePath = object.name;
console.log("File change detected, function execution started");
if (object.resourceState === "not_exists") {
console.log("We deleted a file, exit...");
return;
}
if (path.basename(filePath).startsWith("resized-")) {
console.log("We already renamed that file!");
return;
}
const destBucket = gcs.bucket(bucket);
const tmpFilePath = path.join(os.tmpdir(), path.basename(filePath));
const metadata = { contentType: contentType };
return destBucket
.file(filePath)
.download({
destination: tmpFilePath
})
.then(() => {
return spawn("convert", [tmpFilePath, "-resize", "500x500", tmpFilePath]);
})
.then(() => {
return destBucket.upload(tmpFilePath, {
destination: "resized-" + path.basename(filePath),
metadata: metadata
});
});
});
exports.uploadFile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
if (req.method !== "POST") {
return res.status(500).json({
message: "Not allowed"
});
}
const busboy = new Busboy({ headers: req.headers });
let uploadData = null;
busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
const filepath = path.join(os.tmpdir(), filename);
uploadData = { file: filepath, type: mimetype };
file.pipe(fs.createWriteStream(filepath));
});
busboy.on("finish", () => {
const bucket = gcs.bucket("REDACTED");
bucket
.upload(uploadData.file, {
uploadType: "media",
metadata: {
metadata: {
contentType: uploadData.type
}
}
})
.then(() => {
res.status(200).json({
message: "It worked!"
});
})
.catch(err => {
res.status(500).json({
error: err
});
});
});
busboy.end(req.rawBody);
});
});
My security rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write:if true;
}
}
}

In azure functions (js) POST request after async/await call does not work

I am currently working with azure functions in javascript. In my function, I am first getting a specific element from my CosmoDB (this is the async/await part). I get a result and then I want to do an https POST request. However, my problem is, that it never finished the HTTPs request and I don't really know why. What am I doing wrong?
(As you can see I tried 2 different ways of doing the request, once with the standard https function and the commented out the part with npm request package. However, both ways won't work).
Here is my code:
const CosmosClient = require('#azure/cosmos').CosmosClient;
var https = require('https');
var http = require('http');
var request = require('request');
const endpoint = "someEndpoint";
const masterKey = "anymasterkey";
const database = {
"id": "Database"
};
const container = {
"id": "Container1"
};
const databaseId = database.id;
const containerId = container.id;
const client = new CosmosClient({
endpoint: endpoint,
auth: {
masterKey: masterKey
}
});
module.exports = function (context, req) {
const country = "de";
const bban = 12345678;
const querySpec = {
query: "SELECT * FROM Container1 f WHERE f.country = #country AND f.bban = #bban",
parameters: [{
name: "#country",
value: country
},
{
name: "#bban",
value: bban
}
]
};
getContainers(querySpec).then((results) => {
const result = results[0];
context.log('here before request');
var options = {
host: 'example.com',
port: '80',
path: '/test',
method: 'POST'
};
// Set up the request
var req = http.request(options, (res) => {
var body = "";
context.log('request');
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
context.res = body;
context.done();
});
}).on("error", (error) => {
context.log('error');
context.res = {
status: 500,
body: error
};
context.done();
});
req.end();
// request({
// baseUrl: 'someURL',
// port: 443,
// uri: 'someuri',
// method: 'POST',
// headers: {
// 'Content-Type': 'text/xml;charset=UTF-8',
// 'SOAPAction': 'someaction'
// },
// function (error, response, body) {
// context.log('inside request')
// if (error) {
// context.log('error', error);
// } else {
// context.log('response');
// }
// }
// })
})
};
async function getContainers(querySpec) {
const {container, database} = await init();
return new Promise(async (resolve, reject) => {
const {
result: results
} = await container.items.query(querySpec).toArray();
resolve(results);
})
}
async function init() {
const {
database
} = await client.databases.createIfNotExists({
id: databaseId
});
const {
container
} = await database.containers.createIfNotExists({
id: containerId
});
return {
database,
container
};
}
The last thing that happens is the print of "here before request". After that the function just does nothing until it timesout. So what am I doing wrong? Can't I just this combination of await/async and requests?
As commented you are not sending any data to the POST call. You need to have a req.write before the req.end
req.write(data);
req.end();
That is why the POST call is failing for you. After this fix, it should work

Categories